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