Added support for the take focus protocol.
[wine] / dlls / x11drv / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_SYS_TIME_H
29 # include <sys/time.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <X11/cursorfont.h>
35 #include "ts_xlib.h"
36 #include "ts_xutil.h"
37 #include "ts_shape.h"
38
39 #include "winbase.h"
40 #include "wine/winbase16.h"
41 #include "winreg.h"
42
43 #include "gdi.h"
44 #include "user.h"
45 #include "win.h"
46 #include "wine_gl.h"
47 #include "x11drv.h"
48 #include "xvidmode.h"
49 #include "dga2.h"
50 #include "wine/server.h"
51 #include "wine/debug.h"
52
53 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
54
55 static void (*old_tsx11_lock)(void);
56 static void (*old_tsx11_unlock)(void);
57
58 static CRITICAL_SECTION X11DRV_CritSection = CRITICAL_SECTION_INIT("X11DRV_CritSection");
59
60 Screen *screen;
61 Visual *visual;
62 unsigned int screen_width;
63 unsigned int screen_height;
64 unsigned int screen_depth;
65 Window root_window;
66 int dxgrab, usedga, usexvidmode;
67 int use_take_focus = 1;
68 int managed_mode = 1;
69
70 unsigned int X11DRV_server_startticks;
71
72 static BOOL synchronous;  /* run in synchronous mode? */
73 static char *desktop_geometry;
74 static XVisualInfo *desktop_vi;
75
76 static x11drv_error_callback err_callback;   /* current callback for error */
77 static Display *err_callback_display;        /* display callback is set for */
78 static void *err_callback_arg;               /* error callback argument */
79 static int err_callback_result;              /* error callback result */
80 static int (*old_error_handler)( Display *, XErrorEvent * );
81
82 #define IS_OPTION_TRUE(ch) \
83     ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
84 #define IS_OPTION_FALSE(ch) \
85     ((ch) == 'n' || (ch) == 'N' || (ch) == 'f' || (ch) == 'F' || (ch) == '0')
86
87 /***********************************************************************
88  *              X11DRV_expect_error
89  *
90  * Setup a callback function that will be called on an X error.  The
91  * callback must return non-zero if the error is the one it expected.
92  * This function acquires the x11 lock; X11DRV_check_error must be
93  * called in all cases to release it.
94  */
95 void X11DRV_expect_error( Display *display, x11drv_error_callback callback, void *arg )
96 {
97     wine_tsx11_lock();
98     XSync( display, False );
99     err_callback         = callback;
100     err_callback_display = display;
101     err_callback_arg     = arg;
102     err_callback_result  = 0;
103 }
104
105
106 /***********************************************************************
107  *              X11DRV_check_error
108  *
109  * Check if an expected X11 error occurred; return non-zero if yes.
110  * Also release the x11 lock obtained in X11DRV_expect_error.
111  */
112 int X11DRV_check_error(void)
113 {
114     int ret;
115     XSync( err_callback_display, False );
116     err_callback = NULL;
117     ret = err_callback_result;
118     wine_tsx11_unlock();
119     return ret;
120 }
121
122
123 /***********************************************************************
124  *              error_handler
125  */
126 static int error_handler( Display *display, XErrorEvent *error_evt )
127 {
128     if (err_callback && display == err_callback_display)
129     {
130         if ((err_callback_result = err_callback( display, error_evt, err_callback_arg )))
131         {
132             TRACE( "got expected error\n" );
133             return 0;
134         }
135     }
136     if (synchronous) DebugBreak();  /* force an entry in the debugger */
137     old_error_handler( display, error_evt );
138     return 0;
139 }
140
141 /***********************************************************************
142  *              lock_tsx11
143  */
144 static void lock_tsx11(void)
145 {
146     EnterCriticalSection( &X11DRV_CritSection );
147 }
148
149 /***********************************************************************
150  *              unlock_tsx11
151  */
152 static void unlock_tsx11(void)
153 {
154     LeaveCriticalSection( &X11DRV_CritSection );
155 }
156
157 /***********************************************************************
158  *              get_server_startup
159  *
160  * Get the server startup time
161  * Won't be exact, but should be sufficient
162  */
163 static void get_server_startup(void)
164 {
165     struct timeval t;
166     gettimeofday( &t, NULL );
167     X11DRV_server_startticks = ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - GetTickCount();
168 }
169
170
171 /***********************************************************************
172  *              get_config_key
173  *
174  * Get a config key from either the app-specific or the default config
175  */
176 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
177                                     char *buffer, DWORD size )
178 {
179     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
180     return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
181 }
182
183
184 /***********************************************************************
185  *              setup_options
186  *
187  * Setup the x11drv options.
188  */
189 static void setup_options(void)
190 {
191     char buffer[MAX_PATH+16];
192     HKEY hkey, appkey = 0;
193     DWORD count;
194
195     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", 0, NULL,
196                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
197     {
198         ERR("Cannot create config registry key\n" );
199         ExitProcess(1);
200     }
201
202     /* open the app-specific key */
203
204     if (GetModuleFileNameA( 0, buffer, MAX_PATH ))
205     {
206         HKEY tmpkey;
207         char *p, *appname = buffer;
208         if ((p = strrchr( appname, '/' ))) appname = p + 1;
209         if ((p = strrchr( appname, '\\' ))) appname = p + 1;
210         strcat( appname, "\\x11drv" );
211         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
212         {
213             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
214             RegCloseKey( tmpkey );
215         }
216     }
217
218     /* get the display name */
219
220     strcpy( buffer, "DISPLAY=" );
221     count = sizeof(buffer) - 8;
222     if (!RegQueryValueExA( hkey, "display", 0, NULL, buffer + 8, &count ))
223     {
224         const char *display_name = getenv( "DISPLAY" );
225         if (display_name && strcmp( buffer, display_name ))
226             MESSAGE( "x11drv: Warning: $DISPLAY variable ignored, using '%s' specified in config file\n",
227                      buffer + 8 );
228         putenv( strdup(buffer) );
229     }
230
231     if (!get_config_key( hkey, appkey, "Desktop", buffer, sizeof(buffer) ))
232     {
233         /* Imperfect validation:  If Desktop=N, then we don't turn on
234         ** the --desktop option.  We should really validate for a correct
235         ** sizing entry */
236         if (!IS_OPTION_FALSE(buffer[0])) desktop_geometry = strdup(buffer);
237     }
238
239     if (!get_config_key( hkey, appkey, "Managed", buffer, sizeof(buffer) ))
240         managed_mode = IS_OPTION_TRUE( buffer[0] );
241
242     if (!get_config_key( hkey, appkey, "DXGrab", buffer, sizeof(buffer) ))
243         dxgrab = IS_OPTION_TRUE( buffer[0] );
244
245     if (!get_config_key( hkey, appkey, "UseDGA", buffer, sizeof(buffer) ))
246         usedga = IS_OPTION_TRUE( buffer[0] );
247
248     if (!get_config_key( hkey, appkey, "UseXVidMode", buffer, sizeof(buffer) ))
249         usexvidmode = IS_OPTION_TRUE( buffer[0] );
250
251     if (!get_config_key( hkey, appkey, "UseTakeFocus", buffer, sizeof(buffer) ))
252         use_take_focus = IS_OPTION_TRUE( buffer[0] );
253
254     screen_depth = 0;
255     if (!get_config_key( hkey, appkey, "ScreenDepth", buffer, sizeof(buffer) ))
256         screen_depth = atoi(buffer);
257
258     if (!get_config_key( hkey, appkey, "Synchronous", buffer, sizeof(buffer) ))
259         synchronous = IS_OPTION_TRUE( buffer[0] );
260
261     if (appkey) RegCloseKey( appkey );
262     RegCloseKey( hkey );
263 }
264
265
266 /***********************************************************************
267  *              setup_opengl_visual
268  *
269  * Setup the default visual used for OpenGL and Direct3D, and the desktop
270  * window (if it exists).  If OpenGL isn't available, the visual is simply
271  * set to the default visual for the display
272  */
273 #ifdef HAVE_OPENGL
274 static void setup_opengl_visual( Display *display )
275 {
276     int err_base, evt_base;
277
278     /* In order to support OpenGL or D3D, we require a double-buffered
279      * visual */
280     if (glXQueryExtension(display, &err_base, &evt_base) == True) {
281           int dblBuf[]={GLX_RGBA,GLX_DEPTH_SIZE,16,GLX_DOUBLEBUFFER,None};
282
283           ENTER_GL();
284           desktop_vi = glXChooseVisual(display, DefaultScreen(display), dblBuf);
285           LEAVE_GL();
286     }
287
288     if (desktop_vi != NULL) {
289       visual       = desktop_vi->visual;
290       screen       = ScreenOfDisplay(display, desktop_vi->screen);
291       screen_depth = desktop_vi->depth;
292     }
293 }
294 #endif /* HAVE_OPENGL */
295
296 /***********************************************************************
297  *           X11DRV process initialisation routine
298  */
299 static void process_attach(void)
300 {
301     Display *display;
302
303     get_server_startup();
304     setup_options();
305
306     /* setup TSX11 locking */
307     old_tsx11_lock    = wine_tsx11_lock;
308     old_tsx11_unlock  = wine_tsx11_unlock;
309     wine_tsx11_lock   = lock_tsx11;
310     wine_tsx11_unlock = unlock_tsx11;
311
312     /* Open display */
313
314     if (!(display = TSXOpenDisplay( NULL )))
315     {
316         MESSAGE( "x11drv: Can't open display: %s\n", XDisplayName(NULL) );
317         ExitProcess(1);
318     }
319     fcntl( ConnectionNumber(display), F_SETFD, 1 ); /* set close on exec flag */
320     screen = DefaultScreenOfDisplay( display );
321     visual = DefaultVisual( display, DefaultScreen(display) );
322     root_window = DefaultRootWindow( display );
323     old_error_handler = XSetErrorHandler( error_handler );
324
325     /* Initialize screen depth */
326
327     if (screen_depth)  /* depth specified */
328     {
329         int depth_count, i;
330         int *depth_list = TSXListDepths(display, DefaultScreen(display), &depth_count);
331         for (i = 0; i < depth_count; i++)
332             if (depth_list[i] == screen_depth) break;
333         TSXFree( depth_list );
334         if (i >= depth_count)
335         {
336             MESSAGE( "x11drv: Depth %d not supported on this screen.\n", screen_depth );
337             ExitProcess(1);
338         }
339     }
340     else screen_depth = DefaultDepthOfScreen( screen );
341
342     /* If OpenGL is available, change the default visual, etc as necessary */
343 #ifdef HAVE_OPENGL
344     setup_opengl_visual( display );
345 #endif /* HAVE_OPENGL */
346
347     /* tell the libX11 that we will do input method handling ourselves
348      * that keep libX11 from doing anything whith dead keys, allowing Wine
349      * to have total control over dead keys, that is this line allows
350      * them to work in Wine, even whith a libX11 including the dead key
351      * patches from Th.Quinot (http://Web.FdN.FR/~tquinot/dead-keys.en.html)
352      */
353     TSXOpenIM( display, NULL, NULL, NULL);
354
355     if (synchronous) XSynchronize( display, True );
356
357     screen_width  = WidthOfScreen( screen );
358     screen_height = HeightOfScreen( screen );
359
360     if (desktop_geometry)
361         root_window = X11DRV_create_desktop( desktop_vi, desktop_geometry );
362
363     /* initialize GDI */
364     if(!X11DRV_GDI_Initialize( display ))
365     {
366         ERR( "Couldn't Initialize GDI.\n" );
367         ExitProcess(1);
368     }
369
370 #ifdef HAVE_LIBXXF86VM
371     /* initialize XVidMode */
372     X11DRV_XF86VM_Init();
373 #endif
374 #ifdef HAVE_LIBXXF86DGA2
375     /* initialize DGA2 */
376     X11DRV_XF86DGA2_Init();
377 #endif
378 #ifdef HAVE_OPENGL
379     /* initialize GLX */
380     /*X11DRV_GLX_Init();*/
381 #endif
382
383     /* load display.dll */
384     LoadLibrary16( "display" );
385 }
386
387
388 /***********************************************************************
389  *           X11DRV thread termination routine
390  */
391 static void thread_detach(void)
392 {
393     struct x11drv_thread_data *data = NtCurrentTeb()->driver_data;
394
395     if (data)
396     {
397         CloseHandle( data->display_fd );
398         wine_tsx11_lock();
399         XCloseDisplay( data->display );
400         wine_tsx11_unlock();
401         HeapFree( GetProcessHeap(), 0, data );
402     }
403 }
404
405
406 /***********************************************************************
407  *           X11DRV process termination routine
408  */
409 static void process_detach(void)
410 {
411 #ifdef HAVE_OPENGL
412     /* cleanup GLX */
413     /*X11DRV_GLX_Cleanup();*/
414 #endif
415 #ifdef HAVE_LIBXXF86DGA2
416     /* cleanup DGA2 */
417     X11DRV_XF86DGA2_Cleanup();
418 #endif
419 #ifdef HAVE_LIBXXF86VM
420     /* cleanup XVidMode */
421     X11DRV_XF86VM_Cleanup();
422 #endif
423
424     /* FIXME: should detach all threads */
425     thread_detach();
426
427     /* cleanup GDI */
428     X11DRV_GDI_Finalize();
429
430     /* restore TSX11 locking */
431     wine_tsx11_lock = old_tsx11_lock;
432     wine_tsx11_unlock = old_tsx11_unlock;
433     DeleteCriticalSection( &X11DRV_CritSection );
434 }
435
436
437 /***********************************************************************
438  *           X11DRV thread initialisation routine
439  */
440 struct x11drv_thread_data *x11drv_init_thread_data(void)
441 {
442     struct x11drv_thread_data *data;
443
444     if (!(data = HeapAlloc( GetProcessHeap(), 0, sizeof(*data) )))
445     {
446         ERR( "could not create data\n" );
447         ExitProcess(1);
448     }
449     wine_tsx11_lock();
450     if (!(data->display = XOpenDisplay(NULL)))
451     {
452         wine_tsx11_unlock();
453         MESSAGE( "x11drv: Can't open display: %s\n", XDisplayName(NULL) );
454         ExitProcess(1);
455     }
456     fcntl( ConnectionNumber(data->display), F_SETFD, 1 ); /* set close on exec flag */
457     if (synchronous) XSynchronize( data->display, True );
458     wine_tsx11_unlock();
459     if (wine_server_fd_to_handle( ConnectionNumber(data->display), GENERIC_READ | SYNCHRONIZE,
460                                   FALSE, &data->display_fd ))
461     {
462         MESSAGE( "x11drv: Can't allocate handle for display fd\n" );
463         ExitProcess(1);
464     }
465     data->process_event_count = 0;
466     data->cursor = None;
467     data->cursor_window = None;
468     data->last_focus = 0;
469     NtCurrentTeb()->driver_data = data;
470     return data;
471 }
472
473
474 /***********************************************************************
475  *           X11DRV initialisation routine
476  */
477 BOOL WINAPI X11DRV_Init( HINSTANCE hinst, DWORD reason, LPVOID reserved )
478 {
479     switch(reason)
480     {
481     case DLL_PROCESS_ATTACH:
482         process_attach();
483         break;
484     case DLL_THREAD_DETACH:
485         thread_detach();
486         break;
487     case DLL_PROCESS_DETACH:
488         process_detach();
489         break;
490     }
491     return TRUE;
492 }
493
494 /***********************************************************************
495  *              GetScreenSaveActive (X11DRV.@)
496  *
497  * Returns the active status of the screen saver
498  */
499 BOOL X11DRV_GetScreenSaveActive(void)
500 {
501     int timeout, temp;
502     TSXGetScreenSaver(gdi_display, &timeout, &temp, &temp, &temp);
503     return timeout != 0;
504 }
505
506 /***********************************************************************
507  *              SetScreenSaveActive (X11DRV.@)
508  *
509  * Activate/Deactivate the screen saver
510  */
511 void X11DRV_SetScreenSaveActive(BOOL bActivate)
512 {
513     int timeout, interval, prefer_blanking, allow_exposures;
514     static int last_timeout = 15 * 60;
515
516     TSXGetScreenSaver(gdi_display, &timeout, &interval, &prefer_blanking,
517                       &allow_exposures);
518     if (timeout) last_timeout = timeout;
519
520     timeout = bActivate ? last_timeout : 0;
521     TSXSetScreenSaver(gdi_display, timeout, interval, prefer_blanking,
522                       allow_exposures);
523 }