xref: /aoo41x/main/vcl/unx/gtk/app/gtkinst.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include <osl/module.h>
32 #include <unx/gtk/gtkdata.hxx>
33 #include <unx/gtk/gtkinst.hxx>
34 #include <unx/salobj.h>
35 #include <unx/gtk/gtkframe.hxx>
36 #include <unx/gtk/gtkobject.hxx>
37 #include <unx/gtk/atkbridge.hxx>
38 
39 #include <rtl/strbuf.hxx>
40 
41 #include <rtl/uri.hxx>
42 
43 #if OSL_DEBUG_LEVEL > 1
44 #include <stdio.h>
45 #endif
46 
47 #include <dlfcn.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 
51 GtkHookedYieldMutex::GtkHookedYieldMutex()
52 {
53 }
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 void GtkHookedYieldMutex::ThreadsEnter()
63 {
64 	acquire();
65 	if( !aYieldStack.empty() )
66 	{ /* Previously called ThreadsLeave() */
67 		sal_uLong nCount = aYieldStack.front();
68 		aYieldStack.pop_front();
69 		while( nCount-- > 1 )
70 			acquire();
71 	}
72 }
73 
74 void GtkHookedYieldMutex::ThreadsLeave()
75 {
76 	aYieldStack.push_front( mnCount );
77 
78 #if OSL_DEBUG_LEVEL > 1
79 	if( mnThreadId &&
80 		mnThreadId != vos::OThread::getCurrentIdentifier())
81 		fprintf( stderr, "\n\n--- A different thread owns the mutex ...---\n\n\n");
82 #endif
83 
84 	while( mnCount > 1 )
85 		release();
86 	release();
87 }
88 
89 void GtkHookedYieldMutex::acquire()
90 {
91 	SalYieldMutex::acquire();
92 }
93 
94 void GtkHookedYieldMutex::release()
95 {
96 	SalYieldMutex::release();
97 }
98 
99 extern "C"
100 {
101 	#define GET_YIELD_MUTEX() static_cast<GtkHookedYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())
102 	static void GdkThreadsEnter( void )
103 	{
104 		GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
105 		pYieldMutex->ThreadsEnter();
106 	}
107 	static void GdkThreadsLeave( void )
108 	{
109 		GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX();
110 		pYieldMutex->ThreadsLeave();
111 	}
112 	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 				(GdkLockFn) osl_getAsciiFunctionSymbol( pModule, "gdk_threads_set_lock_functions" );
118 		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 			return false;
124 		}
125 
126 		gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave);
127 #if OSL_DEBUG_LEVEL > 1
128 		fprintf( stderr, "Hooked gdk threads locks\n" );
129 #endif
130 		return true;
131 	}
132 
133     VCLPLUG_GTK_PUBLIC SalInstance* create_SalInstance( oslModule pModule )
134     {
135         /* #i92121# workaround deadlocks in the X11 implementation
136         */
137         static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" );
138         /* #i90094#
139            from now on we know that an X connection will be
140            established, so protect X against itself
141         */
142         if( ! ( pNoXInitThreads && *pNoXInitThreads ) )
143             XInitThreads();
144 
145         const gchar* pVersion = gtk_check_version( 2, 2, 0 );
146         if( pVersion )
147         {
148 #if OSL_DEBUG_LEVEL > 1
149             fprintf( stderr, "gtk version conflict: %s\n", pVersion );
150 #endif
151             return NULL;
152         }
153 
154 		GtkYieldMutex *pYieldMutex;
155 
156         // init gdk thread protection
157 		if ( !g_thread_supported() )
158 			g_thread_init( NULL );
159 
160 		if ( hookLocks( pModule ) )
161 			pYieldMutex = new GtkHookedYieldMutex();
162 		else
163 			pYieldMutex = new GtkYieldMutex();
164 
165 		gdk_threads_init();
166 
167         GtkInstance* pInstance = new GtkInstance( pYieldMutex );
168 #if OSL_DEBUG_LEVEL > 1
169         fprintf( stderr, "creating GtkSalInstance 0x%p\n", pInstance );
170 #endif
171 
172         // initialize SalData
173         GtkData *pSalData = new GtkData();
174         SetSalData( pSalData );
175         pSalData->m_pInstance = pInstance;
176         pSalData->Init();
177         pSalData->initNWF();
178 
179         InitAtkBridge();
180 
181         return pInstance;
182     }
183 }
184 
185 GtkInstance::~GtkInstance()
186 {
187     DeInitAtkBridge();
188 }
189 
190 SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, sal_uLong nStyle )
191 {
192     return new GtkSalFrame( pParent, nStyle );
193 }
194 
195 SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, sal_uLong )
196 {
197 	SalFrame* pFrame = new GtkSalFrame( pParentData );
198 
199 	return pFrame;
200 }
201 
202 SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, sal_Bool bShow )
203 {
204     // there is no method to set a visual for a GtkWidget
205     // so we need the X11SalObject in that case
206     if( pWindowData )
207         return X11SalObject::CreateObject( pParent, pWindowData, bShow );
208 
209     return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow );
210 }
211 
212 extern "C"
213 {
214     typedef void*(* getDefaultFnc)();
215     typedef void(* addItemFnc)(void *, const char *);
216 }
217 
218 void GtkInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType)
219 {
220     rtl::OString sGtkURL;
221     rtl_TextEncoding aSystemEnc = osl_getThreadTextEncoding();
222     if ((aSystemEnc == RTL_TEXTENCODING_UTF8) || (rFileUrl.compareToAscii( "file://", 7 ) !=  0))
223         sGtkURL = rtl::OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8);
224     else
225     {
226         //Non-utf8 locales are a bad idea if trying to work with non-ascii filenames
227         //Decode %XX components
228         rtl::OUString sDecodedUri = Uri::decode(rFileUrl.copy(7), rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8);
229         //Convert back to system locale encoding
230         rtl::OString sSystemUrl = rtl::OUStringToOString(sDecodedUri, aSystemEnc);
231         //Encode to an escaped ASCII-encoded URI
232         gchar *g_uri = g_filename_to_uri(sSystemUrl.getStr(), NULL, NULL);
233         sGtkURL = rtl::OString(g_uri);
234         g_free(g_uri);
235     }
236 #if GTK_CHECK_VERSION(2,10,0)
237     GtkRecentManager *manager = gtk_recent_manager_get_default ();
238     gtk_recent_manager_add_item (manager, sGtkURL);
239     (void)rMimeType;
240 #else
241     static getDefaultFnc sym_gtk_recent_manager_get_default =
242         (getDefaultFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_get_default" );
243 
244     static addItemFnc sym_gtk_recent_manager_add_item =
245         (addItemFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_add_item");
246     if (sym_gtk_recent_manager_get_default && sym_gtk_recent_manager_add_item)
247         sym_gtk_recent_manager_add_item(sym_gtk_recent_manager_get_default(), sGtkURL);
248     else
249         X11SalInstance::AddToRecentDocumentList(rFileUrl, rMimeType);
250 #endif
251 }
252 
253 GtkYieldMutex::GtkYieldMutex()
254 {
255 }
256 
257 void GtkYieldMutex::acquire()
258 {
259     vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier();
260     // protect member manipulation
261     OMutex::acquire();
262     if( mnCount > 0 && mnThreadId == aCurrentThread )
263     {
264         mnCount++;
265         OMutex::release();
266         return;
267     }
268     OMutex::release();
269 
270     // obtain gdk mutex
271     gdk_threads_enter();
272 
273     // obtained gdk mutex, now lock count is one by definition
274     OMutex::acquire();
275     mnCount = 1;
276     mnThreadId = aCurrentThread;
277     OMutex::release();
278 }
279 
280 void GtkYieldMutex::release()
281 {
282     vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier();
283     // protect member manipulation
284     OMutex::acquire();
285     // strange things happen, do nothing if we don't own the mutex
286     if( mnThreadId == aCurrentThread )
287     {
288         mnCount--;
289         if( mnCount == 0 )
290         {
291             gdk_threads_leave();
292             mnThreadId = 0;
293         }
294     }
295     OMutex::release();
296 }
297 
298 sal_Bool GtkYieldMutex::tryToAcquire()
299 {
300     vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier();
301     // protect member manipulation
302     OMutex::acquire();
303     if( mnCount > 0 )
304     {
305         if( mnThreadId == aCurrentThread )
306         {
307             mnCount++;
308             OMutex::release();
309             return sal_True;
310         }
311         else
312         {
313             OMutex::release();
314             return sal_False;
315         }
316     }
317     OMutex::release();
318 
319     // HACK: gdk_threads_mutex is private, we shouldn't use it.
320     // how to we do a try_lock without having a gdk_threads_try_enter ?
321     if( ! g_mutex_trylock( gdk_threads_mutex ) )
322         return sal_False;
323 
324     // obtained gdk mutex, now lock count is one by definition
325     OMutex::acquire();
326     mnCount = 1;
327     mnThreadId = aCurrentThread;
328     OMutex::release();
329 
330     return sal_True;
331 }
332 
333 int GtkYieldMutex::Grab()
334 {
335     // this MUST only be called by gdk/gtk callbacks:
336     // they are entered with gdk mutex locked; the mutex
337     // was unlocked by GtkYieldMutex befor yielding which
338     // is now locked again by gtk implicitly
339 
340     // obtained gdk mutex, now lock count is one by definition
341     OMutex::acquire();
342     int nRet = mnCount;
343     if( mnCount == 0 ) // recursive else
344         mnThreadId = vos::OThread::getCurrentIdentifier();
345 #if OSL_DEBUG_LEVEL > 1
346     else if( mnThreadId != vos::OThread::getCurrentIdentifier() )
347     {
348         fprintf( stderr, "Yield mutex grabbed in different thread !\n" );
349         abort();
350     }
351 #endif
352     mnCount = 1;
353     OMutex::release();
354     return nRet;
355 }
356 
357 void GtkYieldMutex::Ungrab( int nGrabs )
358 {
359     // this MUST only be called when leaving the callback
360     // that locked the mutex with Grab()
361     OMutex::acquire();
362     mnCount = nGrabs;
363     if( mnCount == 0 )
364         mnThreadId = 0;
365     OMutex::release();
366 }
367