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 :
21 : #include <stack>
22 : #include <string.h>
23 : #include <osl/module.h>
24 : #define GLIB_DISABLE_DEPRECATION_WARNINGS
25 : #include <unx/gtk/gtkdata.hxx>
26 : #include <unx/gtk/gtkinst.hxx>
27 : #include <unx/salobj.h>
28 : #include <unx/gtk/gtkframe.hxx>
29 : #include <unx/gtk/gtkobject.hxx>
30 : #include <unx/gtk/atkbridge.hxx>
31 : #include <unx/gtk/gtkprn.hxx>
32 : #include <unx/gtk/gtksalmenu.hxx>
33 : #include <headless/svpvd.hxx>
34 : #include <headless/svpbmp.hxx>
35 : #include <vcl/apptypes.hxx>
36 : #include <generic/genpspgraphics.h>
37 : #include <rtl/strbuf.hxx>
38 :
39 : #include <rtl/uri.hxx>
40 :
41 : #if OSL_DEBUG_LEVEL > 1
42 : #include <stdio.h>
43 : #endif
44 :
45 : #include <dlfcn.h>
46 : #include <fcntl.h>
47 : #include <unistd.h>
48 :
49 : #include "gtkprintwrapper.hxx"
50 :
51 0 : GtkHookedYieldMutex::GtkHookedYieldMutex()
52 : {
53 0 : }
54 :
55 : /*
56 : * These methods always occur in pairs
57 : * A ThreadsEnter is followed by a ThreadsLeave
58 : * We need to queue up the recursive lock count
59 : * for each pair, so we can accurately restore
60 : * it later.
61 : */
62 0 : void GtkHookedYieldMutex::ThreadsEnter()
63 : {
64 0 : acquire();
65 0 : if( !aYieldStack.empty() )
66 : { /* Previously called ThreadsLeave() */
67 0 : sal_uLong nCount = aYieldStack.front();
68 0 : aYieldStack.pop_front();
69 0 : while( nCount-- > 1 )
70 0 : acquire();
71 : }
72 0 : }
73 :
74 0 : void GtkHookedYieldMutex::ThreadsLeave()
75 : {
76 0 : aYieldStack.push_front( mnCount );
77 :
78 : #if OSL_DEBUG_LEVEL > 1
79 : if( mnThreadId &&
80 : mnThreadId != osl::Thread::getCurrentIdentifier())
81 : fprintf( stderr, "\n\n--- A different thread owns the mutex ...---\n\n\n");
82 : #endif
83 :
84 0 : while( mnCount > 1 )
85 0 : release();
86 0 : release();
87 0 : }
88 :
89 0 : void GtkHookedYieldMutex::acquire()
90 : {
91 0 : SalYieldMutex::acquire();
92 0 : }
93 :
94 0 : void GtkHookedYieldMutex::release()
95 : {
96 0 : SalYieldMutex::release();
97 0 : }
98 :
99 : extern "C"
100 : {
101 : #define GET_YIELD_MUTEX() static_cast<GtkHookedYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())
102 0 : static void GdkThreadsEnter( void )
103 : {
104 0 : GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
105 0 : pYieldMutex->ThreadsEnter();
106 0 : }
107 0 : static void GdkThreadsLeave( void )
108 : {
109 0 : GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
110 0 : pYieldMutex->ThreadsLeave();
111 0 : }
112 0 : static bool hookLocks( oslModule pModule )
113 : {
114 : typedef void (*GdkLockFn) (GCallback enter_fn, GCallback leave_fn);
115 :
116 : GdkLockFn gdk_threads_set_lock_functions =
117 0 : (GdkLockFn) osl_getAsciiFunctionSymbol( pModule, "gdk_threads_set_lock_functions" );
118 0 : if ( !gdk_threads_set_lock_functions )
119 : {
120 : #if OSL_DEBUG_LEVEL > 1
121 : fprintf( stderr, "Failed to hook gdk threads locks\n" );
122 : #endif
123 0 : return false;
124 : }
125 :
126 0 : gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave);
127 : #if OSL_DEBUG_LEVEL > 1
128 : fprintf( stderr, "Hooked gdk threads locks\n" );
129 : #endif
130 0 : return true;
131 : }
132 :
133 0 : VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance( oslModule pModule )
134 : {
135 : #if OSL_DEBUG_LEVEL > 1
136 : fprintf( stderr, "create vcl plugin instance with gtk version %d %d %d\n",
137 : (int) gtk_major_version, (int) gtk_minor_version,
138 : (int) gtk_micro_version );
139 : #endif
140 : /* #i92121# workaround deadlocks in the X11 implementation
141 : */
142 0 : static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
143 : /* #i90094#
144 : from now on we know that an X connection will be
145 : established, so protect X against itself
146 : */
147 0 : if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
148 0 : XInitThreads();
149 :
150 : #if GTK_CHECK_VERSION(3,0,0)
151 : const gchar* pVersion = gtk_check_version( 3, 2, 0 );
152 : #else
153 0 : const gchar* pVersion = gtk_check_version( 2, 2, 0 );
154 : #endif
155 0 : if( pVersion )
156 : {
157 : #if OSL_DEBUG_LEVEL > 1
158 : fprintf( stderr, "gtk version conflict: %s\n", pVersion );
159 : #endif
160 0 : return NULL;
161 : }
162 :
163 : GtkYieldMutex *pYieldMutex;
164 :
165 : // init gdk thread protection
166 : if ( !g_thread_supported() )
167 : g_thread_init( NULL );
168 :
169 0 : if ( hookLocks( pModule ) )
170 0 : pYieldMutex = new GtkHookedYieldMutex();
171 : else
172 : #if GTK_CHECK_VERSION(3,0,0)
173 : g_error ("impossible case for gtk3");
174 : #else
175 0 : pYieldMutex = new GtkYieldMutex();
176 : #endif
177 :
178 0 : gdk_threads_init();
179 :
180 0 : GtkInstance* pInstance = new GtkInstance( pYieldMutex );
181 : #if OSL_DEBUG_LEVEL > 1
182 : fprintf( stderr, "creating GtkSalInstance 0x%p\n", pInstance );
183 : #endif
184 :
185 : // initialize SalData
186 0 : GtkData *pSalData = new GtkData( pInstance );
187 0 : pSalData->Init();
188 0 : pSalData->initNWF();
189 :
190 0 : pInstance->Init();
191 :
192 0 : InitAtkBridge();
193 :
194 0 : return pInstance;
195 : }
196 : }
197 :
198 : #if GTK_CHECK_VERSION(3,0,0)
199 : static sal_uInt16 categorizeEvent(const GdkEvent *pEvent)
200 : {
201 : sal_uInt16 nType = 0;
202 : switch( pEvent->type )
203 : {
204 : case GDK_MOTION_NOTIFY:
205 : case GDK_BUTTON_PRESS:
206 : case GDK_2BUTTON_PRESS:
207 : case GDK_3BUTTON_PRESS:
208 : case GDK_BUTTON_RELEASE:
209 : case GDK_ENTER_NOTIFY:
210 : case GDK_LEAVE_NOTIFY:
211 : case GDK_SCROLL:
212 : nType = VCL_INPUT_MOUSE;
213 : break;
214 : case GDK_KEY_PRESS:
215 : case GDK_KEY_RELEASE:
216 : nType = VCL_INPUT_KEYBOARD;
217 : break;
218 : case GDK_EXPOSE:
219 : nType = VCL_INPUT_PAINT;
220 : break;
221 : default:
222 : nType = VCL_INPUT_OTHER;
223 : break;
224 : }
225 : return nType;
226 : }
227 : #endif
228 :
229 0 : GtkInstance::GtkInstance( SalYieldMutex* pMutex )
230 : #if GTK_CHECK_VERSION(3,0,0)
231 : : SvpSalInstance( pMutex )
232 : #else
233 0 : : X11SalInstance( pMutex )
234 : #endif
235 : {
236 0 : }
237 :
238 : // This has to happen after gtk_init has been called by saldata.cxx's
239 : // Init or our handlers just get clobbered.
240 0 : void GtkInstance::Init()
241 : {
242 0 : }
243 :
244 0 : GtkInstance::~GtkInstance()
245 : {
246 0 : while( !m_aTimers.empty() )
247 0 : delete *m_aTimers.begin();
248 0 : DeInitAtkBridge();
249 0 : }
250 :
251 0 : SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, sal_uLong nStyle )
252 : {
253 0 : return new GtkSalFrame( pParent, nStyle );
254 : }
255 :
256 0 : SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, sal_uLong )
257 : {
258 0 : return new GtkSalFrame( pParentData );
259 : }
260 :
261 0 : SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, sal_Bool bShow )
262 : {
263 : #if !GTK_CHECK_VERSION(3,0,0)
264 : // there is no method to set a visual for a GtkWidget
265 : // so we need the X11SalObject in that case
266 0 : if( pWindowData )
267 0 : return X11SalObject::CreateObject( pParent, pWindowData, bShow );
268 : #else
269 : (void)pWindowData;
270 : #warning FIXME: Missing CreateObject functionality ...
271 : #endif
272 :
273 0 : return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow );
274 : }
275 :
276 : extern "C"
277 : {
278 : typedef void*(* getDefaultFnc)();
279 : typedef void(* addItemFnc)(void *, const char *);
280 : }
281 :
282 0 : void GtkInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType)
283 : {
284 0 : rtl::OString sGtkURL;
285 0 : rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding();
286 0 : if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || (rFileUrl.compareToAscii( "file://", 7 ) != 0))
287 0 : sGtkURL = rtl::OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8);
288 : else
289 : {
290 : //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames
291 : //Decode %XX components
292 0 : rtl::OUString sDecodedUri = rtl::Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
293 : //Convert back to system locale encoding
294 0 : rtl::OString sSystemUrl = rtl::OUStringToOString(sDecodedUri, aSystemEnc);
295 : //Encode to an escaped ASCII-encoded URI
296 0 : gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), NULL, NULL);
297 0 : sGtkURL = rtl::OString(g_uri);
298 0 : g_free(g_uri);
299 : }
300 : #if GTK_CHECK_VERSION(2,10,0)
301 0 : GtkRecentManager *manager = gtk_recent_manager_get_default ();
302 0 : gtk_recent_manager_add_item (manager, sGtkURL.getStr());
303 0 : (void)rMimeType;
304 : #else
305 : static getDefaultFnc sym_gtk_recent_manager_get_default =
306 : (getDefaultFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_get_default" );
307 :
308 : static addItemFnc sym_gtk_recent_manager_add_item =
309 : (addItemFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_add_item");
310 : if (sym_gtk_recent_manager_get_default && sym_gtk_recent_manager_add_item)
311 : sym_gtk_recent_manager_add_item(sym_gtk_recent_manager_get_default(), sGtkURL.getStr());
312 : else
313 : X11SalInstance::AddToRecentDocumentList(rFileUrl, rMimeType);
314 : #endif
315 0 : }
316 :
317 : /*
318 : * Obsolete, non-working, and crufty code from the
319 : * beginning of time. When we update our base platform
320 : * we should kill this with extreme prejudice.
321 : */
322 : #if !GTK_CHECK_VERSION(3,0,0)
323 : # define HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
324 : #endif
325 :
326 0 : SalInfoPrinter* GtkInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
327 : ImplJobSetup* pSetupData )
328 : {
329 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
330 0 : mbPrinterInit = true;
331 : // create and initialize SalInfoPrinter
332 0 : PspSalInfoPrinter* pPrinter = new GtkSalInfoPrinter;
333 0 : configurePspInfoPrinter(pPrinter, pQueueInfo, pSetupData);
334 0 : return pPrinter;
335 : #else
336 : return Superclass_t::CreateInfoPrinter( pQueueInfo, pSetupData );
337 : #endif
338 : }
339 :
340 0 : SalPrinter* GtkInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
341 : {
342 : #if defined ENABLE_GTK_PRINT || GTK_CHECK_VERSION(3,0,0)
343 0 : mbPrinterInit = true;
344 0 : fprintf(stderr, "gtk printer\n");
345 0 : return new GtkSalPrinter( pInfoPrinter );
346 : #else
347 : return Superclass_t::CreatePrinter( pInfoPrinter );
348 : #endif
349 : }
350 :
351 :
352 0 : GtkYieldMutex::GtkYieldMutex()
353 : {
354 0 : }
355 :
356 0 : void GtkYieldMutex::acquire()
357 : {
358 : #ifdef HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
359 0 : oslThreadIdentifier aCurrentThread = osl::Thread::getCurrentIdentifier();
360 : // protect member manipulation
361 0 : SolarMutexObject::acquire();
362 0 : if( mnCount > 0 && mnThreadId == aCurrentThread )
363 : {
364 0 : mnCount++;
365 0 : SolarMutexObject::release();
366 0 : return;
367 : }
368 0 : SolarMutexObject::release();
369 :
370 : // obtain gdk mutex
371 0 : gdk_threads_enter();
372 :
373 : // obtained gdk mutex, now lock count is one by definition
374 0 : SolarMutexObject::acquire();
375 0 : mnCount = 1;
376 0 : mnThreadId = aCurrentThread;
377 0 : SolarMutexObject::release();
378 : #else
379 : g_error ("never called");
380 : #endif
381 : }
382 :
383 0 : void GtkYieldMutex::release()
384 : {
385 : #ifdef HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
386 0 : oslThreadIdentifier aCurrentThread = osl::Thread::getCurrentIdentifier();
387 : // protect member manipulation
388 0 : SolarMutexObject::acquire();
389 : // strange things happen, do nothing if we don't own the mutex
390 0 : if( mnThreadId == aCurrentThread )
391 : {
392 0 : mnCount--;
393 0 : if( mnCount == 0 )
394 : {
395 0 : gdk_threads_leave();
396 0 : mnThreadId = 0;
397 : }
398 : }
399 0 : SolarMutexObject::release();
400 : #else
401 : g_error ("never called");
402 : #endif
403 0 : }
404 :
405 0 : sal_Bool GtkYieldMutex::tryToAcquire()
406 : {
407 : #ifdef HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
408 0 : oslThreadIdentifier aCurrentThread = osl::Thread::getCurrentIdentifier();
409 : // protect member manipulation
410 0 : SolarMutexObject::acquire();
411 0 : if( mnCount > 0 )
412 : {
413 0 : if( mnThreadId == aCurrentThread )
414 : {
415 0 : mnCount++;
416 0 : SolarMutexObject::release();
417 0 : return sal_True;
418 : }
419 : else
420 : {
421 0 : SolarMutexObject::release();
422 0 : return sal_False;
423 : }
424 : }
425 0 : SolarMutexObject::release();
426 :
427 : // HACK: gdk_threads_mutex is private, we shouldn't use it.
428 : // how to we do a try_lock without having a gdk_threads_try_enter ?
429 0 : if( ! g_mutex_trylock( gdk_threads_mutex ) )
430 0 : return sal_False;
431 :
432 : // obtained gdk mutex, now lock count is one by definition
433 0 : SolarMutexObject::acquire();
434 0 : mnCount = 1;
435 0 : mnThreadId = aCurrentThread;
436 0 : SolarMutexObject::release();
437 :
438 : #else
439 : g_error ("never called");
440 : #endif
441 0 : return sal_True;
442 : }
443 :
444 0 : int GtkYieldMutex::Grab()
445 : {
446 : #ifdef HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
447 : // this MUST only be called by gdk/gtk callbacks:
448 : // they are entered with gdk mutex locked; the mutex
449 : // was unlocked by GtkYieldMutex befor yielding which
450 : // is now locked again by gtk implicitly
451 :
452 : // obtained gdk mutex, now lock count is one by definition
453 0 : SolarMutexObject::acquire();
454 0 : int nRet = mnCount;
455 0 : if( mnCount == 0 ) // recursive else
456 0 : mnThreadId = osl::Thread::getCurrentIdentifier();
457 : #if OSL_DEBUG_LEVEL > 1
458 : else if( mnThreadId != osl::Thread::getCurrentIdentifier() )
459 : {
460 : fprintf( stderr, "Yield mutex grabbed in different thread !\n" );
461 : abort();
462 : }
463 : #endif
464 0 : mnCount = 1;
465 0 : SolarMutexObject::release();
466 0 : return nRet;
467 : #else
468 : g_error ("never called");
469 : return sal_True;
470 : #endif
471 : }
472 :
473 0 : void GtkYieldMutex::Ungrab( int nGrabs )
474 : {
475 : #ifdef HORRIBLE_OBSOLETE_YIELDMUTEX_IMPL
476 : // this MUST only be called when leaving the callback
477 : // that locked the mutex with Grab()
478 0 : SolarMutexObject::acquire();
479 0 : mnCount = nGrabs;
480 0 : if( mnCount == 0 )
481 0 : mnThreadId = 0;
482 0 : SolarMutexObject::release();
483 : #else
484 : (void)nGrabs;
485 : g_error ("never called");
486 : #endif
487 0 : }
488 :
489 0 : SalVirtualDevice* GtkInstance::CreateVirtualDevice( SalGraphics *pG,
490 : long nDX, long nDY,
491 : sal_uInt16 nBitCount,
492 : const SystemGraphicsData *pGd )
493 : {
494 : #if GTK_CHECK_VERSION(3,0,0)
495 : (void)pG; (void) pGd;
496 : SvpSalVirtualDevice* pNew = new SvpSalVirtualDevice( nBitCount );
497 : pNew->SetSize( nDX, nDY );
498 : return pNew;
499 : #else
500 0 : return X11SalInstance::CreateVirtualDevice( pG, nDX, nDY, nBitCount, pGd );
501 : #endif
502 : }
503 :
504 0 : SalBitmap* GtkInstance::CreateSalBitmap()
505 : {
506 : #if GTK_CHECK_VERSION(3,0,0)
507 : return new SvpSalBitmap();
508 : #else
509 0 : return X11SalInstance::CreateSalBitmap();
510 : #endif
511 : }
512 :
513 : #ifdef ENABLE_GMENU_INTEGRATION
514 :
515 : SalMenu* GtkInstance::CreateMenu( sal_Bool bMenuBar, Menu* pVCLMenu )
516 : {
517 : GtkSalMenu* pSalMenu = new GtkSalMenu( bMenuBar );
518 : pSalMenu->SetMenu( pVCLMenu );
519 : return pSalMenu;
520 : }
521 :
522 : void GtkInstance::DestroyMenu( SalMenu* pMenu )
523 : {
524 : delete pMenu;
525 : }
526 :
527 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* pItemData )
528 : {
529 : return new GtkSalMenuItem( pItemData );
530 : }
531 :
532 : void GtkInstance::DestroyMenuItem( SalMenuItem* pItem )
533 : {
534 : delete pItem;
535 : }
536 :
537 : #else // not ENABLE_GMENU_INTEGRATION
538 :
539 0 : SalMenu* GtkInstance::CreateMenu( sal_Bool, Menu* ) { return NULL; }
540 0 : void GtkInstance::DestroyMenu( SalMenu* ) {}
541 0 : SalMenuItem* GtkInstance::CreateMenuItem( const SalItemParams* ) { return NULL; }
542 0 : void GtkInstance::DestroyMenuItem( SalMenuItem* ) {}
543 :
544 : #endif
545 :
546 0 : SalTimer* GtkInstance::CreateSalTimer()
547 : {
548 0 : GtkSalTimer *pTimer = new GtkSalTimer();
549 0 : m_aTimers.push_back( pTimer );
550 0 : return pTimer;
551 : }
552 :
553 0 : void GtkInstance::RemoveTimer (SalTimer *pTimer)
554 : {
555 0 : std::vector<GtkSalTimer *>::iterator it;
556 0 : it = std::find( m_aTimers.begin(), m_aTimers.end(), pTimer );
557 0 : if( it != m_aTimers.end() )
558 0 : m_aTimers.erase( it );
559 0 : }
560 :
561 0 : void GtkInstance::Yield( bool bWait, bool bHandleAllCurrentEvents )
562 : {
563 0 : GetGtkSalData()->Yield( bWait, bHandleAllCurrentEvents );
564 0 : }
565 :
566 0 : bool GtkInstance::IsTimerExpired()
567 : {
568 0 : for( std::vector<GtkSalTimer *>::iterator it = m_aTimers.begin();
569 0 : it != m_aTimers.end(); ++it )
570 0 : if( (*it)->Expired() )
571 0 : return true;
572 :
573 0 : return false;
574 : }
575 :
576 0 : bool GtkInstance::AnyInput( sal_uInt16 nType )
577 : {
578 0 : if( (nType & VCL_INPUT_TIMER) && IsTimerExpired() )
579 0 : return true;
580 : #if !GTK_CHECK_VERSION(3,0,0)
581 0 : bool bRet = X11SalInstance::AnyInput(nType);
582 : #else
583 : if (!gdk_events_pending())
584 : return false;
585 :
586 : if (nType == VCL_INPUT_ANY)
587 : return true;
588 :
589 : bool bRet = false;
590 : std::stack<GdkEvent*> aEvents;
591 : GdkEvent *pEvent = NULL;
592 : while ((pEvent = gdk_event_get()))
593 : {
594 : aEvents.push(pEvent);
595 : sal_uInt16 nEventType = categorizeEvent(pEvent);
596 : if ( (nEventType & nType) || ( ! nEventType && (nType & VCL_INPUT_OTHER) ) )
597 : {
598 : bRet = true;
599 : break;
600 : }
601 : }
602 :
603 : while (!aEvents.empty())
604 : {
605 : pEvent = aEvents.top();
606 : gdk_event_put(pEvent);
607 : gdk_event_free(pEvent);
608 : aEvents.pop();
609 : }
610 : #endif
611 0 : return bRet;
612 : }
613 :
614 0 : GenPspGraphics *GtkInstance::CreatePrintGraphics()
615 : {
616 0 : return new GenPspGraphics();
617 : }
618 :
619 : boost::shared_ptr<vcl::unx::GtkPrintWrapper>
620 0 : GtkInstance::getPrintWrapper() const
621 : {
622 0 : if (!m_pPrintWrapper)
623 0 : m_pPrintWrapper.reset(new vcl::unx::GtkPrintWrapper);
624 0 : return m_pPrintWrapper;
625 : }
626 :
627 : #if GTK_CHECK_VERSION(3,0,0)
628 : #include "../../../headless/svpinst.cxx"
629 : #endif
630 :
631 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|