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