winex11.drv: Add XEmbed system tray support.
[wine] / dlls / winex11.drv / x11drv_main.c
1 /*
2  * X11DRV initialization code
3  *
4  * Copyright 1998 Patrik Stridvall
5  * Copyright 2000 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include <fcntl.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_SYS_TIME_H
30 # include <sys/time.h>
31 #endif
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <X11/cursorfont.h>
36 #include <X11/Xlib.h>
37 #ifdef HAVE_XKB
38 #include <X11/XKBlib.h>
39 #endif
40 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
41 #include <X11/extensions/Xrender.h>
42 #endif
43
44 #include "windef.h"
45 #include "winbase.h"
46 #include "wine/winbase16.h"
47 #include "winreg.h"
48
49 #include "x11drv.h"
50 #include "xvidmode.h"
51 #include "xrandr.h"
52 #include "wine/server.h"
53 #include "wine/debug.h"
54
55 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
56 WINE_DECLARE_DEBUG_CHANNEL(synchronous);
57
58 static CRITICAL_SECTION X11DRV_CritSection;
59 static CRITICAL_SECTION_DEBUG critsect_debug =
60 {
61     0, 0, &X11DRV_CritSection,
62     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
63       0, 0, { (DWORD_PTR)(__FILE__ ": X11DRV_CritSection") }
64 };
65 static CRITICAL_SECTION X11DRV_CritSection = { &critsect_debug, -1, 0, 0, 0, 0 };
66
67 Screen *screen;
68 Visual *visual;
69 unsigned int screen_width;
70 unsigned int screen_height;
71 unsigned int screen_depth;
72 Window root_window;
73 int dxgrab = 0;
74 int usexvidmode = 1;
75 int usexrandr = 1;
76 int use_xkb = 1;
77 int use_take_focus = 1;
78 int use_primary_selection = 0;
79 int managed_mode = 1;
80 int private_color_map = 0;
81 int client_side_with_core = 1;
82 int client_side_with_render = 1;
83 int client_side_antialias_with_core = 1;
84 int client_side_antialias_with_render = 1;
85 int copy_default_colors = 128;
86 int alloc_system_colors = 256;
87 DWORD thread_data_tls_index = TLS_OUT_OF_INDEXES;
88 int xrender_error_base = 0;
89
90 static BOOL desktop_dbl_buf = TRUE;
91
92 static x11drv_error_callback err_callback;   /* current callback for error */
93 static Display *err_callback_display;        /* display callback is set for */
94 static void *err_callback_arg;               /* error callback argument */
95 static int err_callback_result;              /* error callback result */
96 static unsigned long err_serial;             /* serial number of first request */
97 static int (*old_error_handler)( Display *, XErrorEvent * );
98 static int use_xim = 1;
99 static char input_style[20];
100
101 #define IS_OPTION_TRUE(ch) \
102     ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
103 #define IS_OPTION_FALSE(ch) \
104     ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0')
105
106 Atom X11DRV_Atoms[NB_XATOMS - FIRST_XATOM];
107
108 static const char * const atom_names[NB_XATOMS - FIRST_XATOM] =
109 {
110     "CLIPBOARD",
111     "COMPOUND_TEXT",
112     "MULTIPLE",
113     "SELECTION_DATA",
114     "TARGETS",
115     "TEXT",
116     "UTF8_STRING",
117     "RAW_ASCENT",
118     "RAW_DESCENT",
119     "RAW_CAP_HEIGHT",
120     "WM_PROTOCOLS",
121     "WM_DELETE_WINDOW",
122     "WM_TAKE_FOCUS",
123     "KWM_DOCKWINDOW",
124     "DndProtocol",
125     "DndSelection",
126     "_MOTIF_WM_HINTS",
127     "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
128     "_NET_SYSTEM_TRAY_OPCODE",
129     "_NET_SYSTEM_TRAY_S0",
130     "_NET_WM_MOVERESIZE",
131     "_NET_WM_NAME",
132     "_NET_WM_PID",
133     "_NET_WM_PING",
134     "_NET_WM_STATE",
135     "_NET_WM_STATE_FULLSCREEN",
136     "_NET_WM_WINDOW_TYPE",
137     "_NET_WM_WINDOW_TYPE_UTILITY",
138     "_XEMBED_INFO",
139     "XdndAware",
140     "XdndEnter",
141     "XdndPosition",
142     "XdndStatus",
143     "XdndLeave",
144     "XdndFinished",
145     "XdndDrop",
146     "XdndActionCopy",
147     "XdndActionMove",
148     "XdndActionLink",
149     "XdndActionAsk",
150     "XdndActionPrivate",
151     "XdndSelection",
152     "XdndTarget",
153     "XdndTypeList",
154     "WCF_DIB",
155     "image/gif",
156     "text/html",
157     "text/plain",
158     "text/rtf",
159     "text/richtext"
160 };
161
162 /***********************************************************************
163  *              ignore_error
164  *
165  * Check if the X error is one we can ignore.
166  */
167 static inline BOOL ignore_error( Display *display, XErrorEvent *event )
168 {
169     if (event->request_code == X_SetInputFocus && event->error_code == BadMatch) return TRUE;
170
171     /* ignore a number of errors on gdi display caused by creating/destroying windows */
172     if (display == gdi_display)
173     {
174         if (event->error_code == BadDrawable || event->error_code == BadGC) return TRUE;
175 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
176         if (xrender_error_base)  /* check for XRender errors */
177         {
178             if (event->error_code == xrender_error_base + BadPicture) return TRUE;
179         }
180 #endif
181     }
182     return FALSE;
183 }
184
185
186 /***********************************************************************
187  *              X11DRV_expect_error
188  *
189  * Setup a callback function that will be called on an X error.  The
190  * callback must return non-zero if the error is the one it expected.
191  * This function acquires the x11 lock; X11DRV_check_error must be
192  * called in all cases to release it.
193  */
194 void X11DRV_expect_error( Display *display, x11drv_error_callback callback, void *arg )
195 {
196     wine_tsx11_lock();
197     err_callback         = callback;
198     err_callback_display = display;
199     err_callback_arg     = arg;
200     err_callback_result  = 0;
201     err_serial           = NextRequest(display);
202 }
203
204
205 /***********************************************************************
206  *              X11DRV_check_error
207  *
208  * Check if an expected X11 error occurred; return non-zero if yes.
209  * Also release the x11 lock obtained in X11DRV_expect_error.
210  * The caller is responsible for calling XSync first if necessary.
211  */
212 int X11DRV_check_error(void)
213 {
214     int ret;
215     err_callback = NULL;
216     ret = err_callback_result;
217     wine_tsx11_unlock();
218     return ret;
219 }
220
221
222 /***********************************************************************
223  *              error_handler
224  */
225 static int error_handler( Display *display, XErrorEvent *error_evt )
226 {
227     if (err_callback && display == err_callback_display &&
228         (long)(error_evt->serial - err_serial) >= 0)
229     {
230         if ((err_callback_result = err_callback( display, error_evt, err_callback_arg )))
231         {
232             TRACE( "got expected error %d req %d\n",
233                    error_evt->error_code, error_evt->request_code );
234             return 0;
235         }
236     }
237     if (ignore_error( display, error_evt ))
238     {
239         TRACE( "got ignored error %d req %d\n",
240                error_evt->error_code, error_evt->request_code );
241         return 0;
242     }
243     if (TRACE_ON(synchronous))
244     {
245         ERR( "X protocol error: serial=%ld, request_code=%d - breaking into debugger\n",
246              error_evt->serial, error_evt->request_code );
247         DebugBreak();  /* force an entry in the debugger */
248     }
249     old_error_handler( display, error_evt );
250     return 0;
251 }
252
253 /***********************************************************************
254  *              wine_tsx11_lock   (X11DRV.@)
255  */
256 void wine_tsx11_lock(void)
257 {
258     EnterCriticalSection( &X11DRV_CritSection );
259 }
260
261 /***********************************************************************
262  *              wine_tsx11_unlock   (X11DRV.@)
263  */
264 void wine_tsx11_unlock(void)
265 {
266     LeaveCriticalSection( &X11DRV_CritSection );
267 }
268
269
270 /***********************************************************************
271  *              get_config_key
272  *
273  * Get a config key from either the app-specific or the default config
274  */
275 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
276                                     char *buffer, DWORD size )
277 {
278     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
279     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
280     return ERROR_FILE_NOT_FOUND;
281 }
282
283
284 /***********************************************************************
285  *              setup_options
286  *
287  * Setup the x11drv options.
288  */
289 static void setup_options(void)
290 {
291     char buffer[MAX_PATH+16];
292     HKEY hkey, appkey = 0;
293     DWORD len;
294
295     /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
296     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\X11 Driver", &hkey )) hkey = 0;
297
298     /* open the app-specific key */
299
300     len = (GetModuleFileNameA( 0, buffer, MAX_PATH ));
301     if (len && len < MAX_PATH)
302     {
303         HKEY tmpkey;
304         char *p, *appname = buffer;
305         if ((p = strrchr( appname, '/' ))) appname = p + 1;
306         if ((p = strrchr( appname, '\\' ))) appname = p + 1;
307         strcat( appname, "\\X11 Driver" );
308         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */
309         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
310         {
311             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
312             RegCloseKey( tmpkey );
313         }
314     }
315
316     if (!get_config_key( hkey, appkey, "Managed", buffer, sizeof(buffer) ))
317         managed_mode = IS_OPTION_TRUE( buffer[0] );
318
319     if (!get_config_key( hkey, appkey, "DXGrab", buffer, sizeof(buffer) ))
320         dxgrab = IS_OPTION_TRUE( buffer[0] );
321
322     if (!get_config_key( hkey, appkey, "UseXVidMode", buffer, sizeof(buffer) ))
323         usexvidmode = IS_OPTION_TRUE( buffer[0] );
324
325     if (!get_config_key( hkey, appkey, "UseXRandR", buffer, sizeof(buffer) ))
326         usexrandr = IS_OPTION_TRUE( buffer[0] );
327
328     if (!get_config_key( hkey, appkey, "UseTakeFocus", buffer, sizeof(buffer) ))
329         use_take_focus = IS_OPTION_TRUE( buffer[0] );
330
331     if (!get_config_key( hkey, appkey, "UsePrimarySelection", buffer, sizeof(buffer) ))
332         use_primary_selection = IS_OPTION_TRUE( buffer[0] );
333
334     screen_depth = 0;
335     if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) ))
336         screen_depth = atoi(buffer);
337
338     if (!get_config_key( hkey, appkey, "ClientSideWithCore", buffer, sizeof(buffer) ))
339         client_side_with_core = IS_OPTION_TRUE( buffer[0] );
340
341     if (!get_config_key( hkey, appkey, "ClientSideWithRender", buffer, sizeof(buffer) ))
342         client_side_with_render = IS_OPTION_TRUE( buffer[0] );
343
344     if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithCore", buffer, sizeof(buffer) ))
345         client_side_antialias_with_core = IS_OPTION_TRUE( buffer[0] );
346
347     if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithRender", buffer, sizeof(buffer) ))
348         client_side_antialias_with_render = IS_OPTION_TRUE( buffer[0] );
349
350     if (!get_config_key( hkey, appkey, "DesktopDoubleBuffered", buffer, sizeof(buffer) ))
351         desktop_dbl_buf = IS_OPTION_TRUE( buffer[0] );
352
353     if (!get_config_key( hkey, appkey, "UseXIM", buffer, sizeof(buffer) ))
354         use_xim = IS_OPTION_TRUE( buffer[0] );
355
356     if (!get_config_key( hkey, appkey, "PrivateColorMap", buffer, sizeof(buffer) ))
357         private_color_map = IS_OPTION_TRUE( buffer[0] );
358
359     if (!get_config_key( hkey, appkey, "CopyDefaultColors", buffer, sizeof(buffer) ))
360         copy_default_colors = atoi(buffer);
361
362     if (!get_config_key( hkey, appkey, "AllocSystemColors", buffer, sizeof(buffer) ))
363         alloc_system_colors = atoi(buffer);
364
365     get_config_key( hkey, appkey, "InputStyle", input_style, sizeof(input_style) );
366
367     if (appkey) RegCloseKey( appkey );
368     if (hkey) RegCloseKey( hkey );
369 }
370
371
372 /***********************************************************************
373  *           X11DRV process initialisation routine
374  */
375 static BOOL process_attach(void)
376 {
377     Display *display;
378     XVisualInfo *desktop_vi = NULL;
379
380     setup_options();
381
382     if ((thread_data_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) return FALSE;
383
384     /* Open display */
385
386     if (!(display = XOpenDisplay( NULL ))) return FALSE;
387
388     fcntl( ConnectionNumber(display), F_SETFD, 1 ); /* set close on exec flag */
389     screen = DefaultScreenOfDisplay( display );
390     visual = DefaultVisual( display, DefaultScreen(display) );
391     root_window = DefaultRootWindow( display );
392     gdi_display = display;
393     old_error_handler = XSetErrorHandler( error_handler );
394
395     /* Initialize screen depth */
396
397     if (screen_depth)  /* depth specified */
398     {
399         int depth_count, i;
400         int *depth_list = XListDepths(display, DefaultScreen(display), &depth_count);
401         for (i = 0; i < depth_count; i++)
402             if (depth_list[i] == screen_depth) break;
403         XFree( depth_list );
404         if (i >= depth_count)
405         {
406             WARN( "invalid depth %d, using default\n", screen_depth );
407             screen_depth = 0;
408         }
409     }
410     if (!screen_depth) screen_depth = DefaultDepthOfScreen( screen );
411
412     /* If OpenGL is available, change the default visual, etc as necessary */
413     if (desktop_dbl_buf && (desktop_vi = X11DRV_setup_opengl_visual( display )))
414     {
415         visual       = desktop_vi->visual;
416         screen       = ScreenOfDisplay(display, desktop_vi->screen);
417         screen_depth = desktop_vi->depth;
418         XFree(desktop_vi);
419     }
420
421     XInternAtoms( display, (char **)atom_names, NB_XATOMS - FIRST_XATOM, False, X11DRV_Atoms );
422
423     if (TRACE_ON(synchronous)) XSynchronize( display, True );
424
425     screen_width  = WidthOfScreen( screen );
426     screen_height = HeightOfScreen( screen );
427
428     X11DRV_Settings_Init();
429
430 #ifdef HAVE_LIBXXF86VM
431     /* initialize XVidMode */
432     X11DRV_XF86VM_Init();
433 #endif
434 #ifdef HAVE_LIBXRANDR
435     /* initialize XRandR */
436     X11DRV_XRandR_Init();
437 #endif
438
439     X11DRV_InitKeyboard();
440     X11DRV_InitClipboard();
441
442     return TRUE;
443 }
444
445
446 /***********************************************************************
447  *           X11DRV thread termination routine
448  */
449 static void thread_detach(void)
450 {
451     struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
452
453     if (data)
454     {
455         X11DRV_ResetSelectionOwner();
456         CloseHandle( data->display_fd );
457         wine_tsx11_lock();
458         XCloseDisplay( data->display );
459         /* if (data->xim) XCloseIM( data->xim ); */ /* crashes Xlib */
460         wine_tsx11_unlock();
461         HeapFree( GetProcessHeap(), 0, data );
462     }
463 }
464
465
466 /***********************************************************************
467  *           X11DRV process termination routine
468  */
469 static void process_detach(void)
470 {
471 #ifdef HAVE_LIBXXF86VM
472     /* cleanup XVidMode */
473     X11DRV_XF86VM_Cleanup();
474 #endif
475     if(using_client_side_fonts)
476         X11DRV_XRender_Finalize();
477
478     /* cleanup GDI */
479     X11DRV_GDI_Finalize();
480
481     DeleteCriticalSection( &X11DRV_CritSection );
482     TlsFree( thread_data_tls_index );
483 }
484
485
486 /***********************************************************************
487  *           X11DRV thread initialisation routine
488  */
489 struct x11drv_thread_data *x11drv_init_thread_data(void)
490 {
491     struct x11drv_thread_data *data;
492
493     if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) )))
494     {
495         ERR( "could not create data\n" );
496         ExitProcess(1);
497     }
498     wine_tsx11_lock();
499     if (!(data->display = XOpenDisplay(NULL)))
500     {
501         wine_tsx11_unlock();
502         MESSAGE( "x11drv: Can't open display: %s\n", XDisplayName(NULL) );
503         MESSAGE( "Please ensure that your X server is running and that $DISPLAY is set correctly.\n" );
504         ExitProcess(1);
505     }
506
507     fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */
508
509 #ifdef HAVE_XKB
510     if (use_xkb)
511     {
512         use_xkb = XkbUseExtension( data->display, NULL, NULL );
513         if (use_xkb) XkbSetDetectableAutoRepeat( data->display, True, NULL );
514     }
515 #endif
516
517     if (TRACE_ON(synchronous)) XSynchronize( data->display, True );
518     wine_tsx11_unlock();
519
520     if (use_xim && !(data->xim = X11DRV_SetupXIM( data->display, input_style )))
521         WARN("Input Method is not available\n");
522
523     if (wine_server_fd_to_handle( ConnectionNumber(data->display), GENERIC_READ | SYNCHRONIZE,
524                                   0, &data->display_fd ))
525     {
526         MESSAGE( "x11drv: Can't allocate handle for display fd\n" );
527         ExitProcess(1);
528     }
529     data->process_event_count = 0;
530     data->cursor = None;
531     data->cursor_window = None;
532     data->grab_window = None;
533     data->last_focus = 0;
534     data->selection_wnd = 0;
535     TlsSetValue( thread_data_tls_index, data );
536     return data;
537 }
538
539
540 /***********************************************************************
541  *           X11DRV initialisation routine
542  */
543 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
544 {
545     BOOL ret = TRUE;
546
547     switch(reason)
548     {
549     case DLL_PROCESS_ATTACH:
550         ret = process_attach();
551         break;
552     case DLL_THREAD_DETACH:
553         thread_detach();
554         break;
555     case DLL_PROCESS_DETACH:
556         process_detach();
557         break;
558     }
559     return ret;
560 }
561
562 /***********************************************************************
563  *              GetScreenSaveActive (X11DRV.@)
564  *
565  * Returns the active status of the screen saver
566  */
567 BOOL X11DRV_GetScreenSaveActive(void)
568 {
569     int timeout, temp;
570     wine_tsx11_lock();
571     XGetScreenSaver(gdi_display, &timeout, &temp, &temp, &temp);
572     wine_tsx11_unlock();
573     return timeout != 0;
574 }
575
576 /***********************************************************************
577  *              SetScreenSaveActive (X11DRV.@)
578  *
579  * Activate/Deactivate the screen saver
580  */
581 void X11DRV_SetScreenSaveActive(BOOL bActivate)
582 {
583     int timeout, interval, prefer_blanking, allow_exposures;
584     static int last_timeout = 15 * 60;
585
586     wine_tsx11_lock();
587     XGetScreenSaver(gdi_display, &timeout, &interval, &prefer_blanking,
588                     &allow_exposures);
589     if (timeout) last_timeout = timeout;
590
591     timeout = bActivate ? last_timeout : 0;
592     XSetScreenSaver(gdi_display, timeout, interval, prefer_blanking,
593                     allow_exposures);
594     wine_tsx11_unlock();
595 }