Updated.
[wine] / dlls / x11drv / x11drv_main.c
1 /*
2  * X11DRV initialization code
3  *
4  * Copyright 1998 Patrik Stridvall
5  * Copyright 2000 Alexandre Julliard
6  */
7
8 #include "config.h"
9
10 #ifdef NO_REENTRANT_X11
11 /* Get pointers to the static errno and h_errno variables used by Xlib. This
12    must be done before including <errno.h> makes the variables invisible.  */
13 extern int errno;
14 static int *perrno = &errno;
15 extern int h_errno;
16 static int *ph_errno = &h_errno;
17 #endif  /* NO_REENTRANT_X11 */
18
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/time.h>
24 #include <unistd.h>
25 #include <X11/cursorfont.h>
26 #include "ts_xlib.h"
27 #include "ts_xutil.h"
28 #include "ts_shape.h"
29
30 #include "winbase.h"
31 #include "wine/winbase16.h"
32 #include "winreg.h"
33
34 #include "debugtools.h"
35 #include "gdi.h"
36 #include "options.h"
37 #include "user.h"
38 #include "win.h"
39 #include "wine_gl.h"
40 #include "x11drv.h"
41 #include "xvidmode.h"
42
43 DEFAULT_DEBUG_CHANNEL(x11drv);
44
45 static XKeyboardState keyboard_state;
46 static void (*old_tsx11_lock)(void);
47 static void (*old_tsx11_unlock)(void);
48
49 static CRITICAL_SECTION X11DRV_CritSection = CRITICAL_SECTION_INIT;
50
51 Display *display;
52 Screen *screen;
53 Visual *visual;
54 unsigned int screen_width;
55 unsigned int screen_height;
56 unsigned int screen_depth;
57 Window root_window;
58
59 unsigned int X11DRV_server_startticks;
60
61 #ifdef NO_REENTRANT_X11
62 static int* (*old_errno_location)(void);
63 static int* (*old_h_errno_location)(void);
64
65 /***********************************************************************
66  *           x11_errno_location
67  *
68  * Get the per-thread errno location.
69  */
70 static int *x11_errno_location(void)
71 {
72     /* Use static libc errno while running in Xlib. */
73     if (X11DRV_CritSection.OwningThread == GetCurrentThreadId()) return perrno;
74     return old_errno_location();
75 }
76
77 /***********************************************************************
78  *           x11_h_errno_location
79  *
80  * Get the per-thread h_errno location.
81  */
82 static int *x11_h_errno_location(void)
83 {
84     /* Use static libc h_errno while running in Xlib. */
85     if (X11DRV_CritSection.OwningThread == GetCurrentThreadId()) return ph_errno;
86     return old_h_errno_location();
87 }
88 #endif /* NO_REENTRANT_X11 */
89
90 /***********************************************************************
91  *              error_handler
92  */
93 static int error_handler(Display *display, XErrorEvent *error_evt)
94 {
95     DebugBreak();  /* force an entry in the debugger */
96     return 0;
97 }
98
99 /***********************************************************************
100  *              lock_tsx11
101  */
102 static void lock_tsx11(void)
103 {
104     RtlEnterCriticalSection( &X11DRV_CritSection );
105 }
106
107 /***********************************************************************
108  *              unlock_tsx11
109  */
110 static void unlock_tsx11(void)
111 {
112     RtlLeaveCriticalSection( &X11DRV_CritSection );
113 }
114
115 /***********************************************************************
116  *              get_server_startup
117  *
118  * Get the server startup time 
119  * Won't be exact, but should be sufficient
120  */
121 static void get_server_startup(void)
122 {
123     struct timeval t;
124     gettimeofday( &t, NULL );
125     X11DRV_server_startticks = ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - GetTickCount();
126 }
127
128
129 /***********************************************************************
130  *              setup_options
131  *
132  * Setup the x11drv options.
133  */
134 static void setup_options(void)
135 {
136     char buffer[256];
137     HKEY hkey;
138     DWORD type, count;
139
140     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", 0, NULL,
141                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
142     {
143         ERR("Cannot create config registry key\n" );
144         ExitProcess(1);
145     }
146
147     /* --display option */
148
149     count = sizeof(buffer);
150     if (!RegQueryValueExA( hkey, "display", 0, &type, buffer, &count ))
151     {
152         if (Options.display)
153         {
154             if (strcmp( buffer, Options.display ))
155                 MESSAGE( "%s: warning: --display option ignored, using '%s'\n", argv0, buffer );
156         }
157         else if ((Options.display = getenv( "DISPLAY" )))
158         {
159             if (strcmp( buffer, Options.display ))
160                 MESSAGE( "%s: warning: $DISPLAY variable ignored, using '%s'\n", argv0, buffer );
161         }
162         Options.display = strdup(buffer);
163     }
164     else
165     {
166         if (!Options.display && !(Options.display = getenv( "DISPLAY" )))
167         {
168             MESSAGE( "%s: no display specified\n", argv0 );
169             ExitProcess(1);
170         }
171         RegSetValueExA( hkey, "display", 0, REG_SZ, Options.display, strlen(Options.display)+1 );
172     }
173
174     /* check and set --managed and --desktop options in wine config file
175      * if it was not set on command line */
176
177     if ((!Options.managed) && (Options.desktopGeometry == NULL))
178     {
179         count = sizeof(buffer);
180         if (!RegQueryValueExA( hkey, "managed", 0, &type, buffer, &count ))
181             Options.managed = IS_OPTION_TRUE( buffer[0] );
182
183         count = sizeof(buffer);
184         if (!RegQueryValueExA( hkey, "Desktop", 0, &type, buffer, &count ))
185             /* Imperfect validation:  If Desktop=N, then we don't turn on
186             ** the --desktop option.  We should really validate for a correct
187             ** sizing entry */
188             if (! IS_OPTION_FALSE(buffer[0]))
189                 Options.desktopGeometry = strdup(buffer);
190     }
191
192     if (Options.managed)
193         RegSetValueExA( hkey, "managed", 0, REG_SZ, "y", 2 );
194
195     if (Options.desktopGeometry)
196         RegSetValueExA( hkey, "desktop", 0, REG_SZ, Options.desktopGeometry, strlen(Options.desktopGeometry)+1 );
197     
198     RegCloseKey( hkey );
199 }
200
201    
202 /***********************************************************************
203  *              create_desktop
204  *
205  * Create the desktop window for the --desktop mode.
206  */
207 static void create_desktop( const char *geometry )
208 {
209     int x = 0, y = 0, flags;
210     unsigned int width = 640, height = 480;  /* Default size = 640x480 */
211     char *name = "Wine desktop";
212     XSizeHints *size_hints;
213     XWMHints   *wm_hints;
214     XClassHint *class_hints;
215     XSetWindowAttributes win_attr;
216     XTextProperty window_name;
217     Atom XA_WM_DELETE_WINDOW;
218     /* Used to create the desktop window with a good visual */
219     XVisualInfo *vi = NULL;
220 #ifdef HAVE_OPENGL
221     BOOL dblbuf_visual;
222     int err_base, evt_base;
223     
224     /* Get in wine.ini if the desktop window should have a double-buffered visual or not.
225        But first, test if OpenGL is even supported on the display ! */
226     if (glXQueryExtension(display, &err_base, &evt_base) == True) {
227       dblbuf_visual = PROFILE_GetWineIniBool( "x11drv", "DesktopDoubleBuffered", 0 );
228       if (dblbuf_visual)  {
229         int dblBuf[]={GLX_RGBA,GLX_DEPTH_SIZE,16,GLX_DOUBLEBUFFER,None};
230         
231         ENTER_GL();
232         vi = glXChooseVisual(display, DefaultScreen(display), dblBuf);
233         win_attr.colormap = XCreateColormap(display, RootWindow(display,vi->screen),
234                                             vi->visual, AllocNone);
235         LEAVE_GL();
236       }
237     }
238 #endif /* HAVE_OPENGL */    
239
240     flags = TSXParseGeometry( geometry, &x, &y, &width, &height );
241     screen_width  = width;
242     screen_height = height;
243
244     /* Create window */
245     win_attr.background_pixel = BlackPixel(display, 0);
246     win_attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
247                           PointerMotionMask | ButtonPressMask |
248                           ButtonReleaseMask | EnterWindowMask;
249     win_attr.cursor = TSXCreateFontCursor( display, XC_top_left_arrow );
250
251     if (vi != NULL) {
252       visual       = vi->visual;
253       screen       = ScreenOfDisplay(display, vi->screen);
254       screen_depth = vi->depth;
255     }
256     root_window = TSXCreateWindow( display,
257                                    (vi == NULL ? DefaultRootWindow(display) : RootWindow(display, vi->screen)),
258                                    x, y, width, height, 0,
259                                    (vi == NULL ? CopyFromParent : vi->depth),
260                                    InputOutput,
261                                    (vi == NULL ? CopyFromParent : vi->visual),
262                                    CWBackPixel | CWEventMask | CWCursor | (vi == NULL ? 0 : CWColormap),
263                                    &win_attr );
264   
265     /* Set window manager properties */
266     size_hints  = TSXAllocSizeHints();
267     wm_hints    = TSXAllocWMHints();
268     class_hints = TSXAllocClassHint();
269     if (!size_hints || !wm_hints || !class_hints)
270     {
271         MESSAGE("Not enough memory for window manager hints.\n" );
272         ExitProcess(1);
273     }
274     size_hints->min_width = size_hints->max_width = width;
275     size_hints->min_height = size_hints->max_height = height;
276     size_hints->flags = PMinSize | PMaxSize;
277     if (flags & (XValue | YValue)) size_hints->flags |= USPosition;
278     if (flags & (WidthValue | HeightValue)) size_hints->flags |= USSize;
279     else size_hints->flags |= PSize;
280
281     wm_hints->flags = InputHint | StateHint;
282     wm_hints->input = True;
283     wm_hints->initial_state = NormalState;
284     class_hints->res_name = (char *)argv0;
285     class_hints->res_class = "Wine";
286
287     TSXStringListToTextProperty( &name, 1, &window_name );
288     TSXSetWMProperties( display, root_window, &window_name, &window_name,
289                         NULL, 0, size_hints, wm_hints, class_hints );
290     XA_WM_DELETE_WINDOW = TSXInternAtom( display, "WM_DELETE_WINDOW", False );
291     TSXSetWMProtocols( display, root_window, &XA_WM_DELETE_WINDOW, 1 );
292     TSXFree( size_hints );
293     TSXFree( wm_hints );
294     TSXFree( class_hints );
295
296     /* Map window */
297     TSXMapWindow( display, root_window );
298 }
299
300
301 /***********************************************************************
302  *           X11DRV process initialisation routine
303  */
304 static void process_attach(void)
305 {
306     WND_Driver       = &X11DRV_WND_Driver;
307
308     get_server_startup();
309     setup_options();
310
311     /* setup TSX11 locking */
312 #ifdef NO_REENTRANT_X11
313     old_errno_location = (void *)InterlockedExchange( (PLONG)&wine_errno_location,
314                                                       (LONG)x11_errno_location );
315     old_h_errno_location = (void *)InterlockedExchange( (PLONG)&wine_h_errno_location,
316                                                         (LONG)x11_h_errno_location );
317 #endif /* NO_REENTRANT_X11 */
318     old_tsx11_lock    = wine_tsx11_lock;
319     old_tsx11_unlock  = wine_tsx11_unlock;
320     wine_tsx11_lock   = lock_tsx11;
321     wine_tsx11_unlock = unlock_tsx11;
322
323     /* Open display */
324
325     if (!(display = TSXOpenDisplay( Options.display )))
326     {
327         MESSAGE( "%s: Can't open display: %s\n", argv0, Options.display );
328         ExitProcess(1);
329     }
330     fcntl( ConnectionNumber(display), F_SETFD, 1 ); /* set close on exec flag */
331     screen = DefaultScreenOfDisplay( display );
332     visual = DefaultVisual( display, DefaultScreen(display) );
333     root_window = DefaultRootWindow( display );
334
335     /* Initialize screen depth */
336
337     screen_depth = PROFILE_GetWineIniInt( "x11drv", "ScreenDepth", 0 );
338     if (screen_depth)  /* depth specified */
339     {
340         int depth_count, i;
341         int *depth_list = TSXListDepths(display, DefaultScreen(display), &depth_count);
342         for (i = 0; i < depth_count; i++)
343             if (depth_list[i] == screen_depth) break;
344         TSXFree( depth_list );
345         if (i >= depth_count)
346         {
347             MESSAGE( "%s: Depth %d not supported on this screen.\n", argv0, screen_depth );
348             ExitProcess(1);
349         }
350     }
351     else screen_depth = DefaultDepthOfScreen( screen );
352
353     /* tell the libX11 that we will do input method handling ourselves
354      * that keep libX11 from doing anything whith dead keys, allowing Wine
355      * to have total control over dead keys, that is this line allows
356      * them to work in Wine, even whith a libX11 including the dead key
357      * patches from Th.Quinot (http://Web.FdN.FR/~tquinot/dead-keys.en.html)
358      */
359     TSXOpenIM( display, NULL, NULL, NULL);
360
361     if (Options.synchronous) XSetErrorHandler( error_handler );
362
363     screen_width  = WidthOfScreen( screen );
364     screen_height = HeightOfScreen( screen );
365
366     if (Options.desktopGeometry)
367     {
368         Options.managed = FALSE;
369         create_desktop( Options.desktopGeometry );
370     }
371
372     /* initialize GDI */
373     if(!X11DRV_GDI_Initialize())
374     {
375         MESSAGE( "%s: X11DRV Couldn't Initialize GDI.\n", argv0 );
376         ExitProcess(1);
377     }
378
379
380     /* save keyboard setup */
381     TSXGetKeyboardControl(display, &keyboard_state);
382
383     /* initialize event handling */
384     X11DRV_EVENT_Init();
385
386 #ifdef HAVE_LIBXXF86VM
387     /* initialize XVidMode */
388     X11DRV_XF86VM_Init();
389 #endif
390
391     /* load display.dll */
392     LoadLibrary16( "display" );
393 }
394
395
396 /***********************************************************************
397  *           X11DRV process termination routine
398  */
399 static void process_detach(void)
400 {
401     /* restore keyboard setup */
402     XKeyboardControl keyboard_value;
403   
404     keyboard_value.key_click_percent = keyboard_state.key_click_percent;
405     keyboard_value.bell_percent      = keyboard_state.bell_percent;
406     keyboard_value.bell_pitch        = keyboard_state.bell_pitch;
407     keyboard_value.bell_duration     = keyboard_state.bell_duration;
408     keyboard_value.auto_repeat_mode  = keyboard_state.global_auto_repeat;
409   
410     XChangeKeyboardControl(display, KBKeyClickPercent | KBBellPercent | 
411                            KBBellPitch | KBBellDuration | KBAutoRepeatMode, &keyboard_value);
412
413 #ifdef HAVE_LIBXXF86VM
414     /* cleanup XVidMode */
415     X11DRV_XF86VM_Cleanup();
416 #endif
417
418     /* cleanup GDI */
419     X11DRV_GDI_Finalize();
420
421     /* restore TSX11 locking */
422     wine_tsx11_lock = old_tsx11_lock;
423     wine_tsx11_unlock = old_tsx11_unlock;
424 #ifdef NO_REENTRANT_X11
425     wine_errno_location = old_errno_location;
426     wine_h_errno_location = old_h_errno_location;
427 #endif /* NO_REENTRANT_X11 */
428
429 #if 0  /* FIXME */
430     /* close the display */
431     XCloseDisplay( display );
432     display = NULL;
433
434     WND_Driver       = NULL;
435 #endif
436 }
437
438
439 /***********************************************************************
440  *           X11DRV initialisation routine
441  */
442 BOOL WINAPI X11DRV_Init( HINSTANCE hinst, DWORD reason, LPVOID reserved )
443 {
444     switch(reason)
445     {
446     case DLL_PROCESS_ATTACH:
447         process_attach();
448         break;
449     case DLL_PROCESS_DETACH:
450         process_detach();
451         break;
452     }
453     return TRUE;
454 }
455
456 /***********************************************************************
457  *              X11DRV_GetScreenSaveActive
458  *
459  * Returns the active status of the screen saver
460  */
461 BOOL X11DRV_GetScreenSaveActive(void)
462 {
463     int timeout, temp;
464     TSXGetScreenSaver(display, &timeout, &temp, &temp, &temp);
465     return timeout != 0;
466 }
467
468 /***********************************************************************
469  *              X11DRV_SetScreenSaveActive
470  *
471  * Activate/Deactivate the screen saver
472  */
473 void X11DRV_SetScreenSaveActive(BOOL bActivate)
474 {
475     if(bActivate)
476         TSXActivateScreenSaver(display);
477     else
478         TSXResetScreenSaver(display);
479 }
480
481 /***********************************************************************
482  *              X11DRV_GetScreenSaveTimeout
483  *
484  * Return the screen saver timeout
485  */
486 int X11DRV_GetScreenSaveTimeout(void)
487 {
488     int timeout, temp;
489     TSXGetScreenSaver(display, &timeout, &temp, &temp, &temp);
490     return timeout;
491 }
492
493 /***********************************************************************
494  *              X11DRV_SetScreenSaveTimeout
495  *
496  * Set the screen saver timeout
497  */
498 void X11DRV_SetScreenSaveTimeout(int nTimeout)
499 {
500     /* timeout is a 16bit entity (CARD16) in the protocol, so it should
501      * not get over 32767 or it will get negative. */
502     if (nTimeout>32767) nTimeout = 32767;
503     TSXSetScreenSaver(display, nTimeout, 60, DefaultBlanking, DefaultExposures);
504 }
505
506 /***********************************************************************
507  *              X11DRV_IsSingleWindow
508  */
509 BOOL X11DRV_IsSingleWindow(void)
510 {
511     return (root_window != DefaultRootWindow(display));
512 }