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