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