Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <stack>
21 : #include <string.h>
22 : #include <osl/process.h>
23 : #include <unx/gtk/gtkdata.hxx>
24 : #include <unx/gtk/gtkinst.hxx>
25 : #include <unx/salobj.h>
26 : #include <unx/gtk/gtkgdi.hxx>
27 : #include <unx/gtk/gtkframe.hxx>
28 : #include <unx/gtk/gtkobject.hxx>
29 : #include <unx/gtk/atkbridge.hxx>
30 : #include <unx/gtk/gtkprn.hxx>
31 : #include <unx/gtk/gtksalmenu.hxx>
32 : #include <headless/svpvd.hxx>
33 : #include <headless/svpbmp.hxx>
34 : #include <vcl/apptypes.hxx>
35 : #include <generic/genpspgraphics.h>
36 : #include <rtl/strbuf.hxx>
37 : #include <rtl/uri.hxx>
38 :
39 : #include <vcl/settings.hxx>
40 :
41 : #include <dlfcn.h>
42 : #include <fcntl.h>
43 : #include <unistd.h>
44 :
45 : #include "gtkprintwrapper.hxx"
46 :
47 : extern "C"
48 : {
49 : #define GET_YIELD_MUTEX() static_cast<GtkYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())
50 0 : static void GdkThreadsEnter()
51 : {
52 0 : GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
53 0 : pYieldMutex->ThreadsEnter();
54 0 : }
55 0 : static void GdkThreadsLeave()
56 : {
57 0 : GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
58 0 : pYieldMutex->ThreadsLeave();
59 0 : }
60 :
61 3 : VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance()
62 : {
63 : SAL_INFO(
64 : "vcl.gtk",
65 : "create vcl plugin instance with gtk version " << gtk_major_version
66 : << " " << gtk_minor_version << " " << gtk_micro_version);
67 6 : if( gtk_major_version < 2 || // very unlikely sanity check
68 6 : ( gtk_major_version == 2 && gtk_minor_version < 4 ) )
69 : {
70 0 : g_warning("require a newer gtk than %d.%d for gdk_threads_set_lock_functions", (int) gtk_major_version, gtk_minor_version);
71 0 : return NULL;
72 : }
73 :
74 : /* #i92121# workaround deadlocks in the X11 implementation
75 : */
76 3 : static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
77 : /* #i90094#
78 : from now on we know that an X connection will be
79 : established, so protect X against itself
80 : */
81 3 : if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
82 3 : XInitThreads();
83 :
84 : #if GTK_CHECK_VERSION(3,0,0)
85 : const gchar* pVersion = gtk_check_version( 3, 2, 0 );
86 : #else
87 3 : const gchar* pVersion = gtk_check_version( 2, 2, 0 );
88 : #endif
89 3 : if( pVersion )
90 : {
91 : SAL_WARN("vcl.gtk", "gtk version conflict: " << pVersion);
92 0 : return NULL;
93 : }
94 :
95 : GtkYieldMutex *pYieldMutex;
96 :
97 : // init gdk thread protection
98 : if ( !g_thread_supported() )
99 : g_thread_init( NULL );
100 :
101 3 : gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave);
102 : SAL_INFO("vcl.gtk", "Hooked gdk threads locks");
103 :
104 3 : pYieldMutex = new GtkYieldMutex();
105 :
106 3 : gdk_threads_init();
107 :
108 3 : GtkInstance* pInstance = new GtkInstance( pYieldMutex );
109 : SAL_INFO("vcl.gtk", "creating GtkInstance " << pInstance);
110 :
111 : // Create SalData, this does not leak
112 3 : new GtkData( pInstance );
113 :
114 3 : return pInstance;
115 : }
116 : }
117 :
118 : #if GTK_CHECK_VERSION(3,0,0)
119 : static VclInputFlags categorizeEvent(const GdkEvent *pEvent)
120 : {
121 : VclInputFlags nType = VclInputFlags::NONE;
122 : switch( pEvent->type )
123 : {
124 : case GDK_MOTION_NOTIFY:
125 : case GDK_BUTTON_PRESS:
126 : case GDK_2BUTTON_PRESS:
127 : case GDK_3BUTTON_PRESS:
128 : case GDK_BUTTON_RELEASE:
129 : case GDK_ENTER_NOTIFY:
130 : case GDK_LEAVE_NOTIFY:
131 : case GDK_SCROLL:
132 : nType = VclInputFlags::MOUSE;
133 : break;
134 : case GDK_KEY_PRESS:
135 : case GDK_KEY_RELEASE:
136 : nType = VclInputFlags::KEYBOARD;
137 : break;
138 : case GDK_EXPOSE:
139 : nType = VclInputFlags::PAINT;
140 : break;
141 : default:
142 : nType = VclInputFlags::OTHER;
143 : break;
144 : }
145 : return nType;
146 : }
147 : #endif
148 :
149 3 : GtkInstance::GtkInstance( SalYieldMutex* pMutex )
150 : #if GTK_CHECK_VERSION(3,0,0)
151 : : SvpSalInstance( pMutex )
152 : #else
153 : : X11SalInstance( pMutex )
154 : #endif
155 3 : , bNeedsInit(true)
156 : {
157 3 : }
158 :
159 : //We want to defer initializing gtk until we are after uno has been
160 : //bootstrapped so we can ask the config what the UI language is so that we can
161 : //force that in as $LANGUAGE to get gtk to render widgets RTL if we have a RTL
162 : //UI in a LTR locale
163 3 : void GtkInstance::AfterAppInit()
164 : {
165 3 : OUString aLocaleString(Application::GetSettings().GetUILanguageTag().getGlibcLocaleString(".UTF-8"));
166 3 : if (!aLocaleString.isEmpty())
167 : {
168 3 : OUString envVar("LANGUAGE");
169 3 : osl_setEnvironment(envVar.pData, aLocaleString.pData);
170 : }
171 3 : EnsureInit();
172 3 : }
173 :
174 805 : void GtkInstance::EnsureInit()
175 : {
176 805 : if (!bNeedsInit)
177 1607 : return;
178 : // initialize SalData
179 3 : GtkData *pSalData = GetGtkSalData();
180 3 : pSalData->Init();
181 3 : GtkData::initNWF();
182 :
183 3 : InitAtkBridge();
184 :
185 3 : bNeedsInit = false;
186 : }
187 :
188 3 : GtkInstance::~GtkInstance()
189 : {
190 2 : while( !m_aTimers.empty() )
191 0 : delete *m_aTimers.begin();
192 1 : DeInitAtkBridge();
193 2 : }
194 :
195 16 : SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, sal_uLong nStyle )
196 : {
197 16 : EnsureInit();
198 16 : return new GtkSalFrame( pParent, nStyle );
199 : }
200 :
201 0 : SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, sal_uLong )
202 : {
203 0 : EnsureInit();
204 0 : return new GtkSalFrame( pParentData );
205 : }
206 :
207 0 : SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool bShow )
208 : {
209 0 : EnsureInit();
210 : #if !GTK_CHECK_VERSION(3,0,0)
211 : // there is no method to set a visual for a GtkWidget
212 : // so we need the X11SalObject in that case
213 0 : if( pWindowData )
214 0 : return X11SalObject::CreateObject( pParent, pWindowData, bShow );
215 : #else
216 : (void)pWindowData;
217 : //FIXME: Missing CreateObject functionality ...
218 : #endif
219 :
220 0 : return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow );
221 : }
222 :
223 : extern "C"
224 : {
225 : typedef void*(* getDefaultFnc)();
226 : typedef void(* addItemFnc)(void *, const char *);
227 : }
228 :
229 3 : void GtkInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& rMimeType, const OUString& rDocumentService)
230 : {
231 3 : EnsureInit();
232 3 : OString sGtkURL;
233 3 : rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding();
234 3 : if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || !rFileUrl.startsWith( "file://" ))
235 3 : sGtkURL = OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8);
236 : else
237 : {
238 : //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames
239 : //Decode %XX components
240 0 : OUString sDecodedUri = rtl::Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
241 : //Convert back to system locale encoding
242 0 : OString sSystemUrl = OUStringToOString(sDecodedUri, aSystemEnc);
243 : //Encode to an escaped ASCII-encoded URI
244 0 : gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), NULL, NULL);
245 0 : sGtkURL = OString(g_uri);
246 0 : g_free(g_uri);
247 : }
248 3 : GtkRecentManager *manager = gtk_recent_manager_get_default ();
249 3 : gtk_recent_manager_add_item (manager, sGtkURL.getStr());
250 : (void)rMimeType;
251 3 : (void)rDocumentService;
252 3 : }
253 :
254 2 : SalInfoPrinter* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
255 : ImplJobSetup* pSetupData )
256 : {
257 2 : EnsureInit();
258 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
259 2 : mbPrinterInit = true;
260 : // create and initialize SalInfoPrinter
261 2 : PspSalInfoPrinter* pPrinter = new GtkSalInfoPrinter;
262 2 : configurePspInfoPrinter(pPrinter, pQueueInfo, pSetupData);
263 2 : return pPrinter;
264 : #else
265 : return Superclass_t::CreateInfoPrinter( pQueueInfo, pSetupData );
266 : #endif
267 : }
268 :
269 0 : SalPrinter* GtkInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
270 : {
271 0 : EnsureInit();
272 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
273 0 : mbPrinterInit = true;
274 0 : return new GtkSalPrinter( pInfoPrinter );
275 : #else
276 : return Superclass_t::CreatePrinter( pInfoPrinter );
277 : #endif
278 : }
279 :
280 : /*
281 : * These methods always occur in pairs
282 : * A ThreadsEnter is followed by a ThreadsLeave
283 : * We need to queue up the recursive lock count
284 : * for each pair, so we can accurately restore
285 : * it later.
286 : */
287 0 : void GtkYieldMutex::ThreadsEnter()
288 : {
289 0 : acquire();
290 0 : if( !aYieldStack.empty() )
291 : { /* Previously called ThreadsLeave() */
292 0 : sal_uLong nCount = aYieldStack.front();
293 0 : aYieldStack.pop_front();
294 0 : while( nCount-- > 1 )
295 0 : acquire();
296 : }
297 0 : }
298 :
299 0 : void GtkYieldMutex::ThreadsLeave()
300 : {
301 0 : aYieldStack.push_front( mnCount );
302 :
303 : SAL_WARN_IF(
304 : mnThreadId && mnThreadId != osl::Thread::getCurrentIdentifier(),
305 : "vcl.gtk", "other thread " << mnThreadId << " owns the mutex");
306 :
307 0 : while( mnCount > 1 )
308 0 : release();
309 0 : release();
310 0 : }
311 :
312 177 : SalVirtualDevice* GtkInstance::CreateVirtualDevice( SalGraphics *pG,
313 : long &nDX, long &nDY,
314 : sal_uInt16 nBitCount,
315 : const SystemGraphicsData *pGd )
316 : {
317 177 : EnsureInit();
318 : #if GTK_CHECK_VERSION(3,0,0)
319 : (void)pG; (void) pGd;
320 : SvpSalVirtualDevice* pNew = new SvpSalVirtualDevice( nBitCount );
321 : pNew->SetSize( nDX, nDY );
322 : return pNew;
323 : #else
324 177 : GtkSalGraphics *pGtkSalGraphics = dynamic_cast<GtkSalGraphics*>(pG);
325 : assert(pGtkSalGraphics);
326 : return CreateX11VirtualDevice(pG, nDX, nDY, nBitCount, pGd,
327 177 : new GtkSalGraphics(pGtkSalGraphics->GetGtkFrame(), pGtkSalGraphics->GetGtkWidget()));
328 : #endif
329 : }
330 :
331 185 : SalBitmap* GtkInstance::CreateSalBitmap()
332 : {
333 185 : EnsureInit();
334 : #if GTK_CHECK_VERSION(3,0,0)
335 : return new SvpSalBitmap();
336 : #else
337 185 : return X11SalInstance::CreateSalBitmap();
338 : #endif
339 : }
340 :
341 : #ifdef ENABLE_GMENU_INTEGRATION
342 :
343 43 : SalMenu* GtkInstance::CreateMenu( bool bMenuBar, Menu* pVCLMenu )
344 : {
345 43 : EnsureInit();
346 43 : GtkSalMenu* pSalMenu = new GtkSalMenu( bMenuBar );
347 43 : pSalMenu->SetMenu( pVCLMenu );
348 43 : return pSalMenu;
349 : }
350 :
351 42 : void GtkInstance::DestroyMenu( SalMenu* pMenu )
352 : {
353 42 : EnsureInit();
354 42 : delete pMenu;
355 42 : }
356 :
357 136 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* pItemData )
358 : {
359 136 : EnsureInit();
360 136 : return new GtkSalMenuItem( pItemData );
361 : }
362 :
363 136 : void GtkInstance::DestroyMenuItem( SalMenuItem* pItem )
364 : {
365 136 : EnsureInit();
366 136 : delete pItem;
367 136 : }
368 :
369 : #else // not ENABLE_GMENU_INTEGRATION
370 :
371 : SalMenu* GtkInstance::CreateMenu( bool, Menu* ) { return NULL; }
372 : void GtkInstance::DestroyMenu( SalMenu* ) {}
373 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* ) { return NULL; }
374 : void GtkInstance::DestroyMenuItem( SalMenuItem* ) {}
375 :
376 : #endif
377 :
378 3 : SalTimer* GtkInstance::CreateSalTimer()
379 : {
380 3 : EnsureInit();
381 3 : GtkSalTimer *pTimer = new GtkSalTimer();
382 3 : m_aTimers.push_back( pTimer );
383 3 : return pTimer;
384 : }
385 :
386 1 : void GtkInstance::RemoveTimer (SalTimer *pTimer)
387 : {
388 1 : EnsureInit();
389 1 : std::vector<GtkSalTimer *>::iterator it;
390 1 : it = std::find( m_aTimers.begin(), m_aTimers.end(), pTimer );
391 1 : if( it != m_aTimers.end() )
392 1 : m_aTimers.erase( it );
393 1 : }
394 :
395 0 : void GtkInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
396 : {
397 0 : EnsureInit();
398 0 : GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents );
399 0 : }
400 :
401 0 : bool GtkInstance::IsTimerExpired()
402 : {
403 0 : EnsureInit();
404 0 : for( std::vector<GtkSalTimer *>::iterator it = m_aTimers.begin();
405 0 : it != m_aTimers.end(); ++it )
406 0 : if( (*it)->Expired() )
407 0 : return true;
408 :
409 0 : return false;
410 : }
411 :
412 56 : bool GtkInstance::AnyInput( VclInputFlags nType )
413 : {
414 56 : EnsureInit();
415 56 : if( (nType & VclInputFlags::TIMER) && IsTimerExpired() )
416 0 : return true;
417 : #if !GTK_CHECK_VERSION(3,0,0)
418 56 : bool bRet = X11SalInstance::AnyInput(nType);
419 : #else
420 : if (!gdk_events_pending())
421 : return false;
422 :
423 : if (nType == VCL_INPUT_ANY)
424 : return true;
425 :
426 : bool bRet = false;
427 : std::stack<GdkEvent*> aEvents;
428 : GdkEvent *pEvent = NULL;
429 : while ((pEvent = gdk_event_get()))
430 : {
431 : aEvents.push(pEvent);
432 : VclInputFlags nEventType = categorizeEvent(pEvent);
433 : if ( (nEventType & nType) || ( nEventType == VclInputFlags::NONE && (nType & VclInputFlags::OTHER) ) )
434 : {
435 : bRet = true;
436 : break;
437 : }
438 : }
439 :
440 : while (!aEvents.empty())
441 : {
442 : pEvent = aEvents.top();
443 : gdk_event_put(pEvent);
444 : gdk_event_free(pEvent);
445 : aEvents.pop();
446 : }
447 : #endif
448 56 : return bRet;
449 : }
450 :
451 2 : GenPspGraphics *GtkInstance::CreatePrintGraphics()
452 : {
453 2 : EnsureInit();
454 2 : return new GenPspGraphics();
455 : }
456 :
457 : std::shared_ptr<vcl::unx::GtkPrintWrapper>
458 0 : GtkInstance::getPrintWrapper() const
459 : {
460 0 : if (!m_xPrintWrapper)
461 0 : m_xPrintWrapper.reset(new vcl::unx::GtkPrintWrapper);
462 0 : return m_xPrintWrapper;
463 9 : }
464 :
465 : #if GTK_CHECK_VERSION(3,0,0)
466 : #include "../../../headless/svpinst.cxx"
467 : #endif
468 :
469 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|