user: Add a test for ShowWindow(SW_MAXIMIZE) called on an invisible maximized MDI...
[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_DIALOG",
138     "_NET_WM_WINDOW_TYPE_NORMAL",
139     "_NET_WM_WINDOW_TYPE_UTILITY",
140     "_XEMBED_INFO",
141     "XdndAware",
142     "XdndEnter",
143     "XdndPosition",
144     "XdndStatus",
145     "XdndLeave",
146     "XdndFinished",
147     "XdndDrop",
148     "XdndActionCopy",
149     "XdndActionMove",
150     "XdndActionLink",
151     "XdndActionAsk",
152     "XdndActionPrivate",
153     "XdndSelection",
154     "XdndTarget",
155     "XdndTypeList",
156     "WCF_DIB",
157     "image/gif",
158     "text/html",
159     "text/plain",
160     "text/rtf",
161     "text/richtext"
162 };
163
164 /***********************************************************************
165  *              ignore_error
166  *
167  * Check if the X error is one we can ignore.
168  */
169 static inline BOOL ignore_error( Display *display, XErrorEvent *event )
170 {
171     if (event->request_code == X_SetInputFocus && event->error_code == BadMatch) return TRUE;
172
173     /* ignore a number of errors on gdi display caused by creating/destroying windows */
174     if (display == gdi_display)
175     {
176         if (event->error_code == BadDrawable || event->error_code == BadGC) return TRUE;
177 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
178         if (xrender_error_base)  /* check for XRender errors */
179         {
180             if (event->error_code == xrender_error_base + BadPicture) return TRUE;
181         }
182 #endif
183     }
184     return FALSE;
185 }
186
187
188 /***********************************************************************
189  *              X11DRV_expect_error
190  *
191  * Setup a callback function that will be called on an X error.  The
192  * callback must return non-zero if the error is the one it expected.
193  * This function acquires the x11 lock; X11DRV_check_error must be
194  * called in all cases to release it.
195  */
196 void X11DRV_expect_error( Display *display, x11drv_error_callback callback, void *arg )
197 {
198     wine_tsx11_lock();
199     err_callback         = callback;
200     err_callback_display = display;
201     err_callback_arg     = arg;
202     err_callback_result  = 0;
203     err_serial           = NextRequest(display);
204 }
205
206
207 /***********************************************************************
208  *              X11DRV_check_error
209  *
210  * Check if an expected X11 error occurred; return non-zero if yes.
211  * Also release the x11 lock obtained in X11DRV_expect_error.
212  * The caller is responsible for calling XSync first if necessary.
213  */
214 int X11DRV_check_error(void)
215 {
216     int ret;
217     err_callback = NULL;
218     ret = err_callback_result;
219     wine_tsx11_unlock();
220     return ret;
221 }
222
223
224 /***********************************************************************
225  *              error_handler
226  */
227 static int error_handler( Display *display, XErrorEvent *error_evt )
228 {
229     if (err_callback && display == err_callback_display &&
230         (long)(error_evt->serial - err_serial) >= 0)
231     {
232         if ((err_callback_result = err_callback( display, error_evt, err_callback_arg )))
233         {
234             TRACE( "got expected error %d req %d\n",
235                    error_evt->error_code, error_evt->request_code );
236             return 0;
237         }
238     }
239     if (ignore_error( display, error_evt ))
240     {
241         TRACE( "got ignored error %d req %d\n",
242                error_evt->error_code, error_evt->request_code );
243         return 0;
244     }
245     if (TRACE_ON(synchronous))
246     {
247         ERR( "X protocol error: serial=%ld, request_code=%d - breaking into debugger\n",
248              error_evt->serial, error_evt->request_code );
249         DebugBreak();  /* force an entry in the debugger */
250     }
251     old_error_handler( display, error_evt );
252     return 0;
253 }
254
255 /***********************************************************************
256  *              wine_tsx11_lock   (X11DRV.@)
257  */
258 void wine_tsx11_lock(void)
259 {
260     EnterCriticalSection( &X11DRV_CritSection );
261 }
262
263 /***********************************************************************
264  *              wine_tsx11_unlock   (X11DRV.@)
265  */
266 void wine_tsx11_unlock(void)
267 {
268     LeaveCriticalSection( &X11DRV_CritSection );
269 }
270
271
272 /***********************************************************************
273  *              get_config_key
274  *
275  * Get a config key from either the app-specific or the default config
276  */
277 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
278                                     char *buffer, DWORD size )
279 {
280     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
281     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size )) return 0;
282     return ERROR_FILE_NOT_FOUND;
283 }
284
285
286 /***********************************************************************
287  *              setup_options
288  *
289  * Setup the x11drv options.
290  */
291 static void setup_options(void)
292 {
293     char buffer[MAX_PATH+16];
294     HKEY hkey, appkey = 0;
295     DWORD len;
296
297     /* @@ Wine registry key: HKCU\Software\Wine\X11 Driver */
298     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\X11 Driver", &hkey )) hkey = 0;
299
300     /* open the app-specific key */
301
302     len = (GetModuleFileNameA( 0, buffer, MAX_PATH ));
303     if (len && len < MAX_PATH)
304     {
305         HKEY tmpkey;
306         char *p, *appname = buffer;
307         if ((p = strrchr( appname, '/' ))) appname = p + 1;
308         if ((p = strrchr( appname, '\\' ))) appname = p + 1;
309         strcat( appname, "\\X11 Driver" );
310         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\X11 Driver */
311         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
312         {
313             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
314             RegCloseKey( tmpkey );
315         }
316     }
317
318     if (!get_config_key( hkey, appkey, "Managed", buffer, sizeof(buffer) ))
319         managed_mode = IS_OPTION_TRUE( buffer[0] );
320
321     if (!get_config_key( hkey, appkey, "DXGrab", buffer, sizeof(buffer) ))
322         dxgrab = IS_OPTION_TRUE( buffer[0] );
323
324     if (!get_config_key( hkey, appkey, "UseXVidMode", buffer, sizeof(buffer) ))
325         usexvidmode = IS_OPTION_TRUE( buffer[0] );
326
327     if (!get_config_key( hkey, appkey, "UseXRandR", buffer, sizeof(buffer) ))
328         usexrandr = IS_OPTION_TRUE( buffer[0] );
329
330     if (!get_config_key( hkey, appkey, "UseTakeFocus", buffer, sizeof(buffer) ))
331         use_take_focus = IS_OPTION_TRUE( buffer[0] );
332
333     if (!get_config_key( hkey, appkey, "UsePrimarySelection", buffer, sizeof(buffer) ))
334         use_primary_selection = IS_OPTION_TRUE( buffer[0] );
335
336     screen_depth = 0;
337     if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) ))
338         screen_depth = atoi(buffer);
339
340     if (!get_config_key( hkey, appkey, "ClientSideWithCore", buffer, sizeof(buffer) ))
341         client_side_with_core = IS_OPTION_TRUE( buffer[0] );
342
343     if (!get_config_key( hkey, appkey, "ClientSideWithRender", buffer, sizeof(buffer) ))
344         client_side_with_render = IS_OPTION_TRUE( buffer[0] );
345
346     if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithCore", buffer, sizeof(buffer) ))
347         client_side_antialias_with_core = IS_OPTION_TRUE( buffer[0] );
348
349     if (!get_config_key( hkey, appkey, "ClientSideAntiAliasWithRender", buffer, sizeof(buffer) ))
350         client_side_antialias_with_render = IS_OPTION_TRUE( buffer[0] );
351
352     if (!get_config_key( hkey, appkey, "DesktopDoubleBuffered", buffer, sizeof(buffer) ))
353         desktop_dbl_buf = IS_OPTION_TRUE( buffer[0] );
354
355     if (!get_config_key( hkey, appkey, "UseXIM", buffer, sizeof(buffer) ))
356         use_xim = IS_OPTION_TRUE( buffer[0] );
357
358     if (!get_config_key( hkey, appkey, "PrivateColorMap", buffer, sizeof(buffer) ))
359         private_color_map = IS_OPTION_TRUE( buffer[0] );
360
361     if (!get_config_key( hkey, appkey, "CopyDefaultColors", buffer, sizeof(buffer) ))
362         copy_default_colors = atoi(buffer);
363
364     if (!get_config_key( hkey, appkey, "AllocSystemColors", buffer, sizeof(buffer) ))
365         alloc_system_colors = atoi(buffer);
366
367     get_config_key( hkey, appkey, "InputStyle", input_style, sizeof(input_style) );
368
369     if (appkey) RegCloseKey( appkey );
370     if (hkey) RegCloseKey( hkey );
371 }
372
373
374 /***********************************************************************
375  *           X11DRV process initialisation routine
376  */
377 static BOOL process_attach(void)
378 {
379     Display *display;
380     XVisualInfo *desktop_vi = NULL;
381
382     setup_options();
383
384     if ((thread_data_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) return FALSE;
385
386     /* Open display */
387
388     if (!XInitThreads()) ERR( "XInitThreads failed, trouble ahead\n" );
389
390     if (!(display = XOpenDisplay( NULL ))) return FALSE;
391
392     fcntl( ConnectionNumber(display), F_SETFD, 1 ); /* set close on exec flag */
393     screen = DefaultScreenOfDisplay( display );
394     visual = DefaultVisual( display, DefaultScreen(display) );
395     root_window = DefaultRootWindow( display );
396     gdi_display = display;
397     old_error_handler = XSetErrorHandler( error_handler );
398
399     /* Initialize screen depth */
400
401     if (screen_depth)  /* depth specified */
402     {
403         int depth_count, i;
404         int *depth_list = XListDepths(display, DefaultScreen(display), &depth_count);
405         for (i = 0; i < depth_count; i++)
406             if (depth_list[i] == screen_depth) break;
407         XFree( depth_list );
408         if (i >= depth_count)
409         {
410             WARN( "invalid depth %d, using default\n", screen_depth );
411             screen_depth = 0;
412         }
413     }
414     if (!screen_depth) screen_depth = DefaultDepthOfScreen( screen );
415
416     /* If OpenGL is available, change the default visual, etc as necessary */
417     if (desktop_dbl_buf && (desktop_vi = X11DRV_setup_opengl_visual( display )))
418     {
419         visual       = desktop_vi->visual;
420         screen       = ScreenOfDisplay(display, desktop_vi->screen);
421         screen_depth = desktop_vi->depth;
422         XFree(desktop_vi);
423     }
424
425     XInternAtoms( display, (char **)atom_names, NB_XATOMS - FIRST_XATOM, False, X11DRV_Atoms );
426
427     if (TRACE_ON(synchronous)) XSynchronize( display, True );
428
429     screen_width  = WidthOfScreen( screen );
430     screen_height = HeightOfScreen( screen );
431
432     X11DRV_Settings_Init();
433
434 #ifdef HAVE_LIBXXF86VM
435     /* initialize XVidMode */
436     X11DRV_XF86VM_Init();
437 #endif
438 #ifdef HAVE_LIBXRANDR
439     /* initialize XRandR */
440     X11DRV_XRandR_Init();
441 #endif
442
443     X11DRV_InitKeyboard();
444     X11DRV_InitClipboard();
445
446     return TRUE;
447 }
448
449
450 /***********************************************************************
451  *           X11DRV thread termination routine
452  */
453 static void thread_detach(void)
454 {
455     struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );
456
457     if (data)
458     {
459         X11DRV_ResetSelectionOwner();
460         CloseHandle( data->display_fd );
461         wine_tsx11_lock();
462         XCloseDisplay( data->display );
463         /* if (data->xim) XCloseIM( data->xim ); */ /* crashes Xlib */
464         wine_tsx11_unlock();
465         HeapFree( GetProcessHeap(), 0, data );
466     }
467 }
468
469
470 /***********************************************************************
471  *           X11DRV process termination routine
472  */
473 static void process_detach(void)
474 {
475 #ifdef HAVE_LIBXXF86VM
476     /* cleanup XVidMode */
477     X11DRV_XF86VM_Cleanup();
478 #endif
479     if(using_client_side_fonts)
480         X11DRV_XRender_Finalize();
481
482     /* cleanup GDI */
483     X11DRV_GDI_Finalize();
484
485     DeleteCriticalSection( &X11DRV_CritSection );
486     TlsFree( thread_data_tls_index );
487 }
488
489
490 /***********************************************************************
491  *           X11DRV thread initialisation routine
492  */
493 struct x11drv_thread_data *x11drv_init_thread_data(void)
494 {
495     struct x11drv_thread_data *data;
496
497     if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) )))
498     {
499         ERR( "could not create data\n" );
500         ExitProcess(1);
501     }
502     wine_tsx11_lock();
503     if (!(data->display = XOpenDisplay(NULL)))
504     {
505         wine_tsx11_unlock();
506         MESSAGE( "x11drv: Can't open display: %s\n", XDisplayName(NULL) );
507         MESSAGE( "Please ensure that your X server is running and that $DISPLAY is set correctly.\n" );
508         ExitProcess(1);
509     }
510
511     fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */
512
513 #ifdef HAVE_XKB
514     if (use_xkb)
515     {
516         use_xkb = XkbUseExtension( data->display, NULL, NULL );
517         if (use_xkb) XkbSetDetectableAutoRepeat( data->display, True, NULL );
518     }
519 #endif
520
521     if (TRACE_ON(synchronous)) XSynchronize( data->display, True );
522     wine_tsx11_unlock();
523
524     if (use_xim && !(data->xim = X11DRV_SetupXIM( data->display, input_style )))
525         WARN("Input Method is not available\n");
526
527     if (wine_server_fd_to_handle( ConnectionNumber(data->display), GENERIC_READ | SYNCHRONIZE,
528                                   0, &data->display_fd ))
529     {
530         MESSAGE( "x11drv: Can't allocate handle for display fd\n" );
531         ExitProcess(1);
532     }
533     data->process_event_count = 0;
534     data->cursor = None;
535     data->cursor_window = None;
536     data->grab_window = None;
537     data->last_focus = 0;
538     data->selection_wnd = 0;
539     TlsSetValue( thread_data_tls_index, data );
540     return data;
541 }
542
543
544 /***********************************************************************
545  *           X11DRV initialisation routine
546  */
547 BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
548 {
549     BOOL ret = TRUE;
550
551     switch(reason)
552     {
553     case DLL_PROCESS_ATTACH:
554         ret = process_attach();
555         break;
556     case DLL_THREAD_DETACH:
557         thread_detach();
558         break;
559     case DLL_PROCESS_DETACH:
560         process_detach();
561         break;
562     }
563     return ret;
564 }
565
566 /***********************************************************************
567  *              GetScreenSaveActive (X11DRV.@)
568  *
569  * Returns the active status of the screen saver
570  */
571 BOOL X11DRV_GetScreenSaveActive(void)
572 {
573     int timeout, temp;
574     wine_tsx11_lock();
575     XGetScreenSaver(gdi_display, &timeout, &temp, &temp, &temp);
576     wine_tsx11_unlock();
577     return timeout != 0;
578 }
579
580 /***********************************************************************
581  *              SetScreenSaveActive (X11DRV.@)
582  *
583  * Activate/Deactivate the screen saver
584  */
585 void X11DRV_SetScreenSaveActive(BOOL bActivate)
586 {
587     int timeout, interval, prefer_blanking, allow_exposures;
588     static int last_timeout = 15 * 60;
589
590     wine_tsx11_lock();
591     XGetScreenSaver(gdi_display, &timeout, &interval, &prefer_blanking,
592                     &allow_exposures);
593     if (timeout) last_timeout = timeout;
594
595     timeout = bActivate ? last_timeout : 0;
596     XSetScreenSaver(gdi_display, timeout, interval, prefer_blanking,
597                     allow_exposures);
598     wine_tsx11_unlock();
599 }