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/module.h>
23 : #include <osl/process.h>
24 : #include <unx/gtk/gtkdata.hxx>
25 : #include <unx/gtk/gtkinst.hxx>
26 : #include <unx/salobj.h>
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( void )
51 : {
52 0 : GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
53 0 : pYieldMutex->ThreadsEnter();
54 0 : }
55 0 : static void GdkThreadsLeave( void )
56 : {
57 0 : GtkYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
58 0 : pYieldMutex->ThreadsLeave();
59 0 : }
60 :
61 0 : VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance( oslModule )
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 0 : if( gtk_major_version < 2 || // very unlikely sanity check
68 0 : ( 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 0 : 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 0 : if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
82 0 : XInitThreads();
83 :
84 : #if GTK_CHECK_VERSION(3,0,0)
85 : const gchar* pVersion = gtk_check_version( 3, 2, 0 );
86 : #else
87 0 : const gchar* pVersion = gtk_check_version( 2, 2, 0 );
88 : #endif
89 0 : 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 : #if !GTK_CHECK_VERSION(2,4,0)
102 : #error "Requires gtk 2.4.0+ for lock hooking"
103 : #endif
104 0 : gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave);
105 : SAL_INFO("vcl.gtk", "Hooked gdk threads locks");
106 :
107 0 : pYieldMutex = new GtkYieldMutex();
108 :
109 0 : gdk_threads_init();
110 :
111 0 : GtkInstance* pInstance = new GtkInstance( pYieldMutex );
112 : SAL_INFO("vcl.gtk", "creating GtkSalInstance " << pInstance);
113 :
114 : //Create SalData, this does not leak
115 0 : /*GtkData *pSalData =*/ new GtkData( pInstance );
116 :
117 0 : return pInstance;
118 : }
119 : }
120 :
121 : #if GTK_CHECK_VERSION(3,0,0)
122 : static sal_uInt16 categorizeEvent(const GdkEvent *pEvent)
123 : {
124 : sal_uInt16 nType = 0;
125 : switch( pEvent->type )
126 : {
127 : case GDK_MOTION_NOTIFY:
128 : case GDK_BUTTON_PRESS:
129 : case GDK_2BUTTON_PRESS:
130 : case GDK_3BUTTON_PRESS:
131 : case GDK_BUTTON_RELEASE:
132 : case GDK_ENTER_NOTIFY:
133 : case GDK_LEAVE_NOTIFY:
134 : case GDK_SCROLL:
135 : nType = VCL_INPUT_MOUSE;
136 : break;
137 : case GDK_KEY_PRESS:
138 : case GDK_KEY_RELEASE:
139 : nType = VCL_INPUT_KEYBOARD;
140 : break;
141 : case GDK_EXPOSE:
142 : nType = VCL_INPUT_PAINT;
143 : break;
144 : default:
145 : nType = VCL_INPUT_OTHER;
146 : break;
147 : }
148 : return nType;
149 : }
150 : #endif
151 :
152 0 : GtkInstance::GtkInstance( SalYieldMutex* pMutex )
153 : #if GTK_CHECK_VERSION(3,0,0)
154 : : SvpSalInstance( pMutex )
155 : #else
156 : : X11SalInstance( pMutex )
157 : #endif
158 0 : , bNeedsInit(true)
159 : {
160 0 : }
161 :
162 : //We want to defer initializing gtk until we are after uno has been
163 : //bootstrapped so we can ask the config what the UI language is so that we can
164 : //force that in as $LANGUAGE to get gtk to render widgets RTL if we have a RTL
165 : //UI in a LTR locale
166 0 : void GtkInstance::AfterAppInit()
167 : {
168 0 : OUString aLocaleString(Application::GetSettings().GetUILanguageTag().getGlibcLocaleString(".UTF-8"));
169 0 : if (!aLocaleString.isEmpty())
170 : {
171 0 : OUString envVar("LANGUAGE");
172 0 : osl_setEnvironment(envVar.pData, aLocaleString.pData);
173 : }
174 0 : EnsureInit();
175 0 : }
176 :
177 0 : void GtkInstance::EnsureInit()
178 : {
179 0 : if (!bNeedsInit)
180 0 : return;
181 : // initialize SalData
182 0 : GtkData *pSalData = GetGtkSalData();
183 0 : pSalData->Init();
184 0 : pSalData->initNWF();
185 :
186 0 : InitAtkBridge();
187 :
188 0 : bNeedsInit = false;
189 : }
190 :
191 0 : GtkInstance::~GtkInstance()
192 : {
193 0 : while( !m_aTimers.empty() )
194 0 : delete *m_aTimers.begin();
195 0 : DeInitAtkBridge();
196 0 : }
197 :
198 0 : SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, sal_uLong nStyle )
199 : {
200 0 : EnsureInit();
201 0 : return new GtkSalFrame( pParent, nStyle );
202 : }
203 :
204 0 : SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, sal_uLong )
205 : {
206 0 : EnsureInit();
207 0 : return new GtkSalFrame( pParentData );
208 : }
209 :
210 0 : SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool bShow )
211 : {
212 0 : EnsureInit();
213 : #if !GTK_CHECK_VERSION(3,0,0)
214 : // there is no method to set a visual for a GtkWidget
215 : // so we need the X11SalObject in that case
216 0 : if( pWindowData )
217 0 : return X11SalObject::CreateObject( pParent, pWindowData, bShow );
218 : #else
219 : (void)pWindowData;
220 : //FIXME: Missing CreateObject functionality ...
221 : #endif
222 :
223 0 : return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow );
224 : }
225 :
226 : extern "C"
227 : {
228 : typedef void*(* getDefaultFnc)();
229 : typedef void(* addItemFnc)(void *, const char *);
230 : }
231 :
232 0 : void GtkInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& rMimeType, const OUString& rDocumentService)
233 : {
234 0 : EnsureInit();
235 0 : OString sGtkURL;
236 0 : rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding();
237 0 : if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || !rFileUrl.startsWith( "file://" ))
238 0 : sGtkURL = OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8);
239 : else
240 : {
241 : //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames
242 : //Decode %XX components
243 0 : OUString sDecodedUri = rtl::Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
244 : //Convert back to system locale encoding
245 0 : OString sSystemUrl = OUStringToOString(sDecodedUri, aSystemEnc);
246 : //Encode to an escaped ASCII-encoded URI
247 0 : gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), NULL, NULL);
248 0 : sGtkURL = OString(g_uri);
249 0 : g_free(g_uri);
250 : }
251 : #if GTK_CHECK_VERSION(2,10,0)
252 0 : GtkRecentManager *manager = gtk_recent_manager_get_default ();
253 0 : gtk_recent_manager_add_item (manager, sGtkURL.getStr());
254 : (void)rMimeType;
255 0 : (void)rDocumentService;
256 : #else
257 : static getDefaultFnc sym_gtk_recent_manager_get_default =
258 : (getDefaultFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_get_default" );
259 :
260 : static addItemFnc sym_gtk_recent_manager_add_item =
261 : (addItemFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_add_item");
262 : if (sym_gtk_recent_manager_get_default && sym_gtk_recent_manager_add_item)
263 : sym_gtk_recent_manager_add_item(sym_gtk_recent_manager_get_default(), sGtkURL.getStr());
264 : else
265 : X11SalInstance::AddToRecentDocumentList(rFileUrl, rMimeType, rDocumentService);
266 : #endif
267 0 : }
268 :
269 0 : SalInfoPrinter* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
270 : ImplJobSetup* pSetupData )
271 : {
272 0 : EnsureInit();
273 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
274 0 : mbPrinterInit = true;
275 : // create and initialize SalInfoPrinter
276 0 : PspSalInfoPrinter* pPrinter = new GtkSalInfoPrinter;
277 0 : configurePspInfoPrinter(pPrinter, pQueueInfo, pSetupData);
278 0 : return pPrinter;
279 : #else
280 : return Superclass_t::CreateInfoPrinter( pQueueInfo, pSetupData );
281 : #endif
282 : }
283 :
284 0 : SalPrinter* GtkInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
285 : {
286 0 : EnsureInit();
287 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
288 0 : mbPrinterInit = true;
289 0 : return new GtkSalPrinter( pInfoPrinter );
290 : #else
291 : return Superclass_t::CreatePrinter( pInfoPrinter );
292 : #endif
293 : }
294 :
295 0 : GtkYieldMutex::GtkYieldMutex()
296 : {
297 0 : }
298 :
299 0 : void GtkYieldMutex::acquire()
300 : {
301 0 : SalYieldMutex::acquire();
302 0 : }
303 :
304 0 : void GtkYieldMutex::release()
305 : {
306 0 : SalYieldMutex::release();
307 0 : }
308 :
309 : /*
310 : * These methods always occur in pairs
311 : * A ThreadsEnter is followed by a ThreadsLeave
312 : * We need to queue up the recursive lock count
313 : * for each pair, so we can accurately restore
314 : * it later.
315 : */
316 0 : void GtkYieldMutex::ThreadsEnter()
317 : {
318 0 : acquire();
319 0 : if( !aYieldStack.empty() )
320 : { /* Previously called ThreadsLeave() */
321 0 : sal_uLong nCount = aYieldStack.front();
322 0 : aYieldStack.pop_front();
323 0 : while( nCount-- > 1 )
324 0 : acquire();
325 : }
326 0 : }
327 :
328 0 : void GtkYieldMutex::ThreadsLeave()
329 : {
330 0 : aYieldStack.push_front( mnCount );
331 :
332 : SAL_WARN_IF(
333 : mnThreadId && mnThreadId != osl::Thread::getCurrentIdentifier(),
334 : "vcl.gtk", "other thread " << mnThreadId << " owns the mutex");
335 :
336 0 : while( mnCount > 1 )
337 0 : release();
338 0 : release();
339 0 : }
340 :
341 0 : SalVirtualDevice* GtkInstance::CreateVirtualDevice( SalGraphics *pG,
342 : long nDX, long nDY,
343 : sal_uInt16 nBitCount,
344 : const SystemGraphicsData *pGd )
345 : {
346 0 : EnsureInit();
347 : #if GTK_CHECK_VERSION(3,0,0)
348 : (void)pG; (void) pGd;
349 : SvpSalVirtualDevice* pNew = new SvpSalVirtualDevice( nBitCount );
350 : pNew->SetSize( nDX, nDY );
351 : return pNew;
352 : #else
353 0 : return X11SalInstance::CreateVirtualDevice( pG, nDX, nDY, nBitCount, pGd );
354 : #endif
355 : }
356 :
357 0 : SalBitmap* GtkInstance::CreateSalBitmap()
358 : {
359 0 : EnsureInit();
360 : #if GTK_CHECK_VERSION(3,0,0)
361 : return new SvpSalBitmap();
362 : #else
363 0 : return X11SalInstance::CreateSalBitmap();
364 : #endif
365 : }
366 :
367 : #ifdef ENABLE_GMENU_INTEGRATION
368 :
369 0 : SalMenu* GtkInstance::CreateMenu( bool bMenuBar, Menu* pVCLMenu )
370 : {
371 0 : EnsureInit();
372 0 : GtkSalMenu* pSalMenu = new GtkSalMenu( bMenuBar );
373 0 : pSalMenu->SetMenu( pVCLMenu );
374 0 : return pSalMenu;
375 : }
376 :
377 0 : void GtkInstance::DestroyMenu( SalMenu* pMenu )
378 : {
379 0 : EnsureInit();
380 0 : delete pMenu;
381 0 : }
382 :
383 0 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* pItemData )
384 : {
385 0 : EnsureInit();
386 0 : return new GtkSalMenuItem( pItemData );
387 : }
388 :
389 0 : void GtkInstance::DestroyMenuItem( SalMenuItem* pItem )
390 : {
391 0 : EnsureInit();
392 0 : delete pItem;
393 0 : }
394 :
395 : #else // not ENABLE_GMENU_INTEGRATION
396 :
397 : SalMenu* GtkInstance::CreateMenu( bool, Menu* ) { return NULL; }
398 : void GtkInstance::DestroyMenu( SalMenu* ) {}
399 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* ) { return NULL; }
400 : void GtkInstance::DestroyMenuItem( SalMenuItem* ) {}
401 :
402 : #endif
403 :
404 0 : SalTimer* GtkInstance::CreateSalTimer()
405 : {
406 0 : EnsureInit();
407 0 : GtkSalTimer *pTimer = new GtkSalTimer();
408 0 : m_aTimers.push_back( pTimer );
409 0 : return pTimer;
410 : }
411 :
412 0 : void GtkInstance::RemoveTimer (SalTimer *pTimer)
413 : {
414 0 : EnsureInit();
415 0 : std::vector<GtkSalTimer *>::iterator it;
416 0 : it = std::find( m_aTimers.begin(), m_aTimers.end(), pTimer );
417 0 : if( it != m_aTimers.end() )
418 0 : m_aTimers.erase( it );
419 0 : }
420 :
421 0 : void GtkInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
422 : {
423 0 : EnsureInit();
424 0 : GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents );
425 0 : }
426 :
427 0 : bool GtkInstance::IsTimerExpired()
428 : {
429 0 : EnsureInit();
430 0 : for( std::vector<GtkSalTimer *>::iterator it = m_aTimers.begin();
431 0 : it != m_aTimers.end(); ++it )
432 0 : if( (*it)->Expired() )
433 0 : return true;
434 :
435 0 : return false;
436 : }
437 :
438 0 : bool GtkInstance::AnyInput( sal_uInt16 nType )
439 : {
440 0 : EnsureInit();
441 0 : if( (nType & VCL_INPUT_TIMER) && IsTimerExpired() )
442 0 : return true;
443 : #if !GTK_CHECK_VERSION(3,0,0)
444 0 : bool bRet = X11SalInstance::AnyInput(nType);
445 : #else
446 : if (!gdk_events_pending())
447 : return false;
448 :
449 : if (nType == VCL_INPUT_ANY)
450 : return true;
451 :
452 : bool bRet = false;
453 : std::stack<GdkEvent*> aEvents;
454 : GdkEvent *pEvent = NULL;
455 : while ((pEvent = gdk_event_get()))
456 : {
457 : aEvents.push(pEvent);
458 : sal_uInt16 nEventType = categorizeEvent(pEvent);
459 : if ( (nEventType & nType) || ( ! nEventType && (nType & VCL_INPUT_OTHER) ) )
460 : {
461 : bRet = true;
462 : break;
463 : }
464 : }
465 :
466 : while (!aEvents.empty())
467 : {
468 : pEvent = aEvents.top();
469 : gdk_event_put(pEvent);
470 : gdk_event_free(pEvent);
471 : aEvents.pop();
472 : }
473 : #endif
474 0 : return bRet;
475 : }
476 :
477 0 : GenPspGraphics *GtkInstance::CreatePrintGraphics()
478 : {
479 0 : EnsureInit();
480 0 : return new GenPspGraphics();
481 : }
482 :
483 : boost::shared_ptr<vcl::unx::GtkPrintWrapper>
484 0 : GtkInstance::getPrintWrapper() const
485 : {
486 0 : if (!m_pPrintWrapper)
487 0 : m_pPrintWrapper.reset(new vcl::unx::GtkPrintWrapper);
488 0 : return m_pPrintWrapper;
489 0 : }
490 :
491 : #if GTK_CHECK_VERSION(3,0,0)
492 : #include "../../../headless/svpinst.cxx"
493 : #endif
494 :
495 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|