bump product version to 5.0.4.1
[LibreOffice.git] / extensions / source / plugin / unx / npwrap.cxx
blob3c7d9d7145fecb9c5817a204cf8f5791087cae6f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #ifdef AIX
30 #define _LINUX_SOURCE_COMPAT
31 #include <sys/timer.h>
32 #undef _LINUX_SOURCE_COMPAT
33 #endif
34 #include <errno.h>
35 #include <dlfcn.h>
36 #include <unistd.h>
37 #include <sys/poll.h>
38 #include <fcntl.h>
39 #include <signal.h>
41 #include <plugin/unx/plugcon.hxx>
43 #include <osl/file.h>
44 #include <osl/module.h>
45 #include <sal/log.hxx>
47 #include <config_vclplug.h>
49 #include <npwrap.hxx>
51 PluginConnector* pConnector = NULL;
53 int nAppArguments = 0;
54 char** pAppArguments = NULL;
55 Display* pAppDisplay = NULL;
56 Display* pXtAppDisplay = NULL;
58 extern oslModule pPluginLib;
59 extern NPError (*pNP_Shutdown)();
61 XtAppContext app_context;
62 Widget topLevel = NULL, topBox = NULL;
63 int wakeup_fd[2] = { 0, 0 };
64 static bool bPluginAppQuit = false;
66 static long GlobalConnectionLostHdl( void* /*pInst*/, void* /*pArg*/ )
68 SAL_WARN("extensions.plugin", "pluginapp exiting due to connection lost");
70 bool bSuccess = (4 == write(wakeup_fd[1], "xxxx", 4 ));
71 SAL_WARN_IF(!bSuccess, "extensions.plugin", "short write");
72 return 0;
75 extern "C"
77 static int plugin_x_error_handler( Display*, XErrorEvent* )
79 return 0;
82 #if ! ENABLE_GTK
83 static void ThreadEventHandler( XtPointer /*client_data*/, int* /*source*/, XtInputId* id )
85 char buf[256];
86 // clear pipe
87 int len, nLast = -1;
89 while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 )
90 nLast = len-1;
91 if( ! bPluginAppQuit )
93 if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector )
94 pConnector->CallWorkHandler();
95 else
97 // it seems you can use XtRemoveInput only
98 // safely from within the callback
99 // why is that ?
100 SAL_INFO("extensions.plugin", "removing wakeup pipe");
101 XtRemoveInput( *id );
102 XtAppSetExitFlag( app_context );
103 bPluginAppQuit = true;
105 delete pConnector;
106 pConnector = NULL;
110 #endif
114 IMPL_LINK( PluginConnector, NewMessageHdl, Mediator*, /*pMediator*/ )
116 (void) this; // loplugin:staticmethods
117 SAL_INFO("extensions.plugin", "new message handler");
118 bool bSuccess = (4 == write(wakeup_fd[1], "cccc", 4));
119 SAL_WARN_IF(!bSuccess, "extensions.plugin", "short write");
120 return 0;
124 Widget createSubWidget( char* /*pPluginText*/, Widget shell, Window aParentWindow )
126 Widget newWidget = XtVaCreateManagedWidget(
127 #if defined USE_MOTIF
128 "drawingArea",
129 xmDrawingAreaWidgetClass,
130 #else
132 compositeWidgetClass,
133 #endif
134 shell,
135 XtNwidth, 200,
136 XtNheight, 200,
137 (char *)NULL );
138 XtRealizeWidget( shell );
139 XtRealizeWidget( newWidget );
141 SAL_INFO(
142 "extensions.plugin",
143 "reparenting new widget " << XtWindow( newWidget ) << " to "
144 << aParentWindow);
145 XReparentWindow( pXtAppDisplay,
146 XtWindow( shell ),
147 aParentWindow,
148 0, 0 );
149 XtMapWidget( shell );
150 XtMapWidget( newWidget );
151 XRaiseWindow( pXtAppDisplay, XtWindow( shell ) );
152 XSync( pXtAppDisplay, False );
154 return newWidget;
157 void* CreateNewShell( void** pShellReturn, Window aParentWindow )
159 String n, c;
160 XtGetApplicationNameAndClass(pXtAppDisplay, &n, &c);
162 Widget newShell =
163 XtVaAppCreateShell( "pane", c,
164 topLevelShellWidgetClass,
165 pXtAppDisplay,
166 XtNwidth, 200,
167 XtNheight, 200,
168 XtNoverrideRedirect, True,
169 (char *)NULL );
170 *pShellReturn = newShell;
172 char pText[1024];
173 sprintf( pText, "starting plugin %s ...", pAppArguments[2] );
175 Widget newWidget = createSubWidget( pText, newShell, aParentWindow );
177 return newWidget;
180 static oslModule LoadModule( const char* pPath )
182 OUString sSystemPath( OUString::createFromAscii( pPath ) );
183 OUString sFileURL;
184 osl_getFileURLFromSystemPath( sSystemPath.pData, &sFileURL.pData );
186 oslModule pLib = osl_loadModule( sFileURL.pData, SAL_LOADMODULE_LAZY );
187 SAL_INFO_IF(!pLib, "extensions.plugin", "could not open " << pPath);
188 return pLib;
191 // Unix specific implementation
192 static void CheckPlugin( const char* pPath )
194 oslModule pLib = LoadModule( pPath );
195 if (pLib != 0)
197 char*(*pNP_GetMIMEDescription)() = reinterpret_cast<char*(*)()>(
198 osl_getAsciiFunctionSymbol( pLib, "NP_GetMIMEDescription" ));
199 if( pNP_GetMIMEDescription )
200 printf( "%s\n", pNP_GetMIMEDescription() );
201 else
202 SAL_WARN(
203 "extensions.plugin",
204 "could not get symbol NP_GetMIMEDescription " << dlerror());
205 osl_unloadModule( pLib );
209 #if OSL_DEBUG_LEVEL > 1 && defined LINUX
210 #include <execinfo.h>
211 #endif
213 extern "C" {
215 static void signal_handler( int nSig )
217 #if OSL_DEBUG_LEVEL > 1
218 fprintf( stderr, "caught signal %d, exiting\n", nSig );
219 #ifdef LINUX
220 void* pStack[64];
221 int nStackLevels = backtrace( pStack, SAL_N_ELEMENTS(pStack) );
222 backtrace_symbols_fd( pStack, nStackLevels, STDERR_FILENO );
223 #endif
224 #endif
225 if( pConnector )
227 // ensure that a read on the other side will wakeup
228 delete pConnector;
229 pConnector = NULL;
232 _exit(nSig);
235 #if ENABLE_GTK
237 static gboolean noClosure( gpointer )
239 return sal_True;
242 // Xt events
243 static gboolean prepareXtEvent( GSource*, gint* )
245 int nMask = XtAppPending( app_context );
246 return (nMask & XtIMAll) != 0;
249 static gboolean checkXtEvent( GSource* )
251 int nMask = XtAppPending( app_context );
252 return (nMask & XtIMAll) != 0;
255 static gboolean dispatchXtEvent( GSource*, GSourceFunc, gpointer )
257 XtAppProcessEvent( app_context, XtIMAll );
258 return sal_True;
261 static GSourceFuncs aXtEventFuncs =
263 prepareXtEvent,
264 checkXtEvent,
265 dispatchXtEvent,
266 NULL,
267 noClosure,
268 NULL
271 static gboolean pollXtTimerCallback(gpointer)
273 for(int i = 0; i < 5; i++)
275 if( (XtAppPending(app_context) & (XtIMAll & ~XtIMXEvent)) == 0 )
276 break;
277 XtAppProcessEvent(app_context, XtIMAll & ~XtIMXEvent);
279 return sal_True;
282 static gboolean prepareWakeupEvent( GSource*, gint* )
284 struct pollfd aPoll = { wakeup_fd[0], POLLIN, 0 };
285 (void)poll(&aPoll, 1, 0);
286 return (aPoll.revents & POLLIN ) != 0;
289 static gboolean checkWakeupEvent( GSource* pSource )
291 gint nDum = 0;
292 return prepareWakeupEvent( pSource, &nDum );
295 static gboolean dispatchWakeupEvent( GSource*, GSourceFunc, gpointer )
297 char buf[256];
298 // clear pipe
299 int len, nLast = -1;
301 while( (len = read( wakeup_fd[0], buf, sizeof( buf ) ) ) > 0 )
302 nLast = len-1;
303 if( ( nLast == -1 || buf[nLast] != 'x' ) && pConnector )
304 pConnector->CallWorkHandler();
305 else
307 XtAppSetExitFlag( app_context );
308 bPluginAppQuit = true;
310 delete pConnector;
311 pConnector = NULL;
314 return sal_True;
317 static GSourceFuncs aWakeupEventFuncs = {
318 prepareWakeupEvent,
319 checkWakeupEvent,
320 dispatchWakeupEvent,
321 NULL,
322 noClosure,
323 NULL
326 #endif // GTK
330 int main( int argc, char **argv)
332 struct sigaction aSigAction;
333 aSigAction.sa_handler = signal_handler;
334 sigemptyset( &aSigAction.sa_mask );
335 aSigAction.sa_flags = SA_NOCLDSTOP;
336 sigaction( SIGSEGV, &aSigAction, NULL );
337 sigaction( SIGBUS, &aSigAction, NULL );
338 sigaction( SIGABRT, &aSigAction, NULL );
339 sigaction( SIGTERM, &aSigAction, NULL );
340 sigaction( SIGILL, &aSigAction, NULL );
342 int nArg = (argc < 3) ? 1 : 2;
343 char* pBaseName = argv[nArg] + strlen(argv[nArg]);
344 while( pBaseName > argv[nArg] && pBaseName[-1] != '/' )
345 pBaseName--;
346 LoadAdditionalLibs( pBaseName );
348 if( argc == 2 )
350 CheckPlugin(argv[1]);
351 exit(0);
353 nAppArguments = argc;
354 pAppArguments = argv;
356 XSetErrorHandler( plugin_x_error_handler );
358 if( pipe( wakeup_fd ) )
360 SAL_WARN("extensions.plugin", "could not pipe()");
361 return 1;
363 // initialize 'wakeup' pipe.
364 int flags;
366 // set close-on-exec descriptor flag.
367 if ((flags = fcntl (wakeup_fd[0], F_GETFD)) != -1)
369 flags |= FD_CLOEXEC;
370 (void)fcntl(wakeup_fd[0], F_SETFD, flags);
372 if ((flags = fcntl (wakeup_fd[1], F_GETFD)) != -1)
374 flags |= FD_CLOEXEC;
375 (void)fcntl(wakeup_fd[1], F_SETFD, flags);
378 // set non-blocking I/O flag.
379 if ((flags = fcntl (wakeup_fd[0], F_GETFL)) != -1)
381 flags |= O_NONBLOCK;
382 (void)fcntl(wakeup_fd[0], F_SETFL, flags);
384 if ((flags = fcntl (wakeup_fd[1], F_GETFL)) != -1)
386 flags |= O_NONBLOCK;
387 (void)fcntl(wakeup_fd[1], F_SETFL, flags);
390 pPluginLib = LoadModule( argv[2] );
391 if( ! pPluginLib )
393 exit(255);
395 int nSocket = atol( argv[1] );
397 #if ENABLE_GTK
398 g_thread_init(NULL);
399 gtk_init(&argc, &argv);
400 #endif
402 pConnector = new PluginConnector( nSocket );
403 pConnector->SetConnectionLostHdl( Link<>( NULL, GlobalConnectionLostHdl ) );
405 XtSetLanguageProc( NULL, NULL, NULL );
407 XtToolkitInitialize();
408 app_context = XtCreateApplicationContext();
409 pXtAppDisplay = XtOpenDisplay( app_context, NULL, "SOPlugin", "SOPlugin", NULL, 0, &argc, argv );
412 #if ENABLE_GTK
413 // integrate Xt events into GTK event loop
414 GPollFD aXtPollDesc, aWakeupPollDesc;
416 GSource* pXTSource = g_source_new( &aXtEventFuncs, sizeof(GSource) );
417 if( !pXTSource )
419 SAL_WARN("extensions.plugin", "could not get Xt GSource");
420 return 1;
423 g_source_set_priority( pXTSource, GDK_PRIORITY_EVENTS );
424 g_source_set_can_recurse( pXTSource, sal_True );
425 g_source_attach( pXTSource, NULL );
426 aXtPollDesc.fd = ConnectionNumber( pXtAppDisplay );
427 aXtPollDesc.events = G_IO_IN;
428 aXtPollDesc.revents = 0;
429 g_source_add_poll( pXTSource, &aXtPollDesc );
431 gint xt_polling_timer_id = g_timeout_add( 25, pollXtTimerCallback, NULL);
432 // Initialize wakeup events listener
433 GSource *pWakeupSource = g_source_new( &aWakeupEventFuncs, sizeof(GSource) );
434 if ( pWakeupSource == NULL )
436 SAL_WARN("extensions.plugin", "could not get wakeup source");
437 return 1;
439 g_source_set_priority( pWakeupSource, GDK_PRIORITY_EVENTS);
440 g_source_attach( pWakeupSource, NULL );
441 aWakeupPollDesc.fd = wakeup_fd[0];
442 aWakeupPollDesc.events = G_IO_IN;
443 aWakeupPollDesc.revents = 0;
444 g_source_add_poll( pWakeupSource, &aWakeupPollDesc );
446 pAppDisplay = gdk_x11_display_get_xdisplay( gdk_display_get_default() );
447 #else
448 pAppDisplay = pXtAppDisplay;
449 XtAppAddInput( app_context,
450 wakeup_fd[0],
451 (XtPointer)XtInputReadMask,
452 ThreadEventHandler, NULL );
453 #endif
455 // send that we are ready to go
456 MediatorMessage* pMessage =
457 pConnector->Transact( "init req", 8,
458 NULL );
459 delete pMessage;
461 #if OSL_DEBUG_LEVEL > 3
462 int nPID = getpid();
463 int nChild = fork();
464 if( nChild == 0 )
466 char pidbuf[16];
467 char* pArgs[] = { "xterm", "-sl", "2000", "-sb", "-e", "gdb", "pluginapp.bin", pidbuf, NULL };
468 sprintf( pidbuf, "%d", nPID );
469 execvp( pArgs[0], pArgs );
470 _exit(255);
472 else
473 sleep( 10 );
474 #endif
477 * Loop for events.
479 // for some reason XtAppSetExitFlag won't quit the application
480 // in ThreadEventHandler most of times; Xt will hang in select
481 // (hat is in XtAppNextEvent). Have our own mainloop instead
482 // of XtAppMainLoop
485 #if ENABLE_GTK
486 g_main_context_iteration( NULL, sal_True );
487 #else
488 XtAppProcessEvent( app_context, XtIMAll );
489 #endif
490 } while( ! XtAppGetExitFlag( app_context ) && ! bPluginAppQuit );
492 SAL_INFO("extensions.plugin", "left plugin app main loop");
494 #if ENABLE_GTK
495 g_source_remove(xt_polling_timer_id);
496 #endif
498 pNP_Shutdown();
499 SAL_INFO("extensions.plugin", "NP_Shutdown done");
500 osl_unloadModule( pPluginLib );
501 SAL_INFO("extensions.plugin", "plugin close");
503 close( wakeup_fd[0] );
504 close( wakeup_fd[1] );
506 return 0;
509 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */