Fixed some issues found by winapi_check.
[wine] / dlls / x11drv / window.c
1 /*
2  * Window related functions
3  *
4  * Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard
5  * Copyright 1993 David Metcalfe
6  * Copyright 1995, 1996 Alex Korobka
7  */
8
9 #include "config.h"
10
11 #include "ts_xlib.h"
12 #include "ts_xutil.h"
13
14 #include "winbase.h"
15 #include "wingdi.h"
16 #include "winuser.h"
17
18 #include "debugtools.h"
19 #include "x11drv.h"
20 #include "win.h"
21 #include "options.h"
22
23 DEFAULT_DEBUG_CHANNEL(win);
24
25 extern Cursor X11DRV_MOUSE_XCursor;  /* current X cursor */
26 extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP );
27
28 #define HAS_DLGFRAME(style,exStyle) \
29 ((!((style) & WS_THICKFRAME)) && (((style) & WS_DLGFRAME) || ((exStyle) & WS_EX_DLGMODALFRAME)))
30
31 /* X context to associate a hwnd to an X window */
32 XContext winContext = 0;
33
34 Atom wmProtocols = None;
35 Atom wmDeleteWindow = None;
36 Atom dndProtocol = None;
37 Atom dndSelection = None;
38 Atom wmChangeState = None;
39 Atom kwmDockWindow = None;
40 Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */
41
42
43 /***********************************************************************
44  *              register_window
45  *
46  * Associate an X window to a HWND.
47  */
48 static void register_window( HWND hwnd, Window win )
49 {
50     if (!winContext) winContext = TSXUniqueContext();
51     TSXSaveContext( display, win, winContext, (char *)hwnd );
52     TSXSetWMProtocols( display, win, &wmDeleteWindow, 1 );
53 }
54
55
56 /***********************************************************************
57  *              set_wm_hint
58  *
59  * Set a window manager hint.
60  */
61 static void set_wm_hint( Window win, int hint, int val )
62 {
63     XWMHints* wm_hints = TSXGetWMHints( display, win );
64     if (!wm_hints) wm_hints = TSXAllocWMHints();
65     if (wm_hints)
66     {
67         wm_hints->flags = hint;
68         switch( hint )
69         {
70         case InputHint:
71             wm_hints->input = val;
72             break;
73
74         case StateHint:
75             wm_hints->initial_state = val;
76             break;
77
78         case IconPixmapHint:
79             wm_hints->icon_pixmap = (Pixmap)val;
80             break;
81
82         case IconWindowHint:
83             wm_hints->icon_window = (Window)val;
84             break;
85         }
86         TSXSetWMHints( display, win, wm_hints );
87         TSXFree(wm_hints);
88     }
89 }
90
91
92 /***********************************************************************
93  *              set_icon_hints
94  *
95  * Set the icon wm hints
96  */
97 static void set_icon_hints( WND *wndPtr, XWMHints *hints )
98 {
99     X11DRV_WND_DATA *data = wndPtr->pDriverData;
100     HICON hIcon = GetClassLongA( wndPtr->hwndSelf, GCL_HICON );
101
102     if (data->hWMIconBitmap) DeleteObject( data->hWMIconBitmap );
103     if (data->hWMIconMask) DeleteObject( data->hWMIconMask);
104
105     if (!hIcon)
106     {
107         data->hWMIconBitmap = 0;
108         data->hWMIconMask = 0;
109         hints->flags &= ~(IconPixmapHint | IconMaskHint);
110     }
111     else
112     {
113         HBITMAP hbmOrig;
114         RECT rcMask;
115         BITMAP bmMask;
116         ICONINFO ii;
117         HDC hDC;
118
119         GetIconInfo(hIcon, &ii);
120
121         X11DRV_CreateBitmap(ii.hbmMask);
122         X11DRV_CreateBitmap(ii.hbmColor);
123
124         GetObjectA(ii.hbmMask, sizeof(bmMask), &bmMask);
125         rcMask.top    = 0;
126         rcMask.left   = 0;
127         rcMask.right  = bmMask.bmWidth;
128         rcMask.bottom = bmMask.bmHeight;
129
130         hDC = CreateCompatibleDC(0);
131         hbmOrig = SelectObject(hDC, ii.hbmMask);
132         InvertRect(hDC, &rcMask);
133         SelectObject(hDC, hbmOrig);
134         DeleteDC(hDC);
135
136         data->hWMIconBitmap = ii.hbmColor;
137         data->hWMIconMask = ii.hbmMask;
138
139         hints->icon_pixmap = X11DRV_BITMAP_Pixmap(data->hWMIconBitmap);
140         hints->icon_mask = X11DRV_BITMAP_Pixmap(data->hWMIconMask);
141         hints->flags |= IconPixmapHint | IconMaskHint;
142     }
143 }
144
145
146 /***********************************************************************
147  *              dock_window
148  *
149  * Set the X Property of the window that tells the windowmanager we really
150  * want to be in the systray
151  *
152  * KDE: set "KWM_DOCKWINDOW", type "KWM_DOCKWINDOW" to 1 before a window is 
153  *      mapped.
154  *
155  * all others: to be added ;)
156  */
157 inline static void dock_window( Window win )
158 {
159     int data = 1;
160     if (kwmDockWindow != None)
161         TSXChangeProperty( display, win, kwmDockWindow, kwmDockWindow,
162                            32, PropModeReplace, (char*)&data, 1 );
163     if (_kde_net_wm_system_tray_window_for != None)
164         TSXChangeProperty( display, win, _kde_net_wm_system_tray_window_for, XA_WINDOW,
165                            32, PropModeReplace, (char*)&win, 1 );
166 }
167
168
169 /**********************************************************************
170  *              create_desktop
171  */
172 static void create_desktop(WND *wndPtr)
173 {
174     X11DRV_WND_DATA *data = wndPtr->pDriverData;
175
176     wmProtocols = TSXInternAtom( display, "WM_PROTOCOLS", True );
177     wmDeleteWindow = TSXInternAtom( display, "WM_DELETE_WINDOW", True );
178     dndProtocol = TSXInternAtom( display, "DndProtocol" , False );
179     dndSelection = TSXInternAtom( display, "DndSelection" , False );
180     wmChangeState = TSXInternAtom (display, "WM_CHANGE_STATE", False);
181     kwmDockWindow = TSXInternAtom( display, "KWM_DOCKWINDOW", False );
182     _kde_net_wm_system_tray_window_for = TSXInternAtom( display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False );
183
184     data->window = root_window;
185     if (root_window != DefaultRootWindow(display)) wndPtr->flags |= WIN_NATIVE;
186     register_window( wndPtr->hwndSelf, root_window );
187 }
188
189
190 /**********************************************************************
191  *              CreateWindow   (X11DRV.@)
192  */
193 BOOL X11DRV_CreateWindow( HWND hwnd )
194 {
195     X11DRV_WND_DATA *data;
196     WND *wndPtr = WIN_FindWndPtr( hwnd );
197     int x = wndPtr->rectWindow.left;
198     int y = wndPtr->rectWindow.top;
199     int cx = wndPtr->rectWindow.right - wndPtr->rectWindow.left;
200     int cy = wndPtr->rectWindow.bottom - wndPtr->rectWindow.top;
201
202     if (!(data = HeapAlloc(GetProcessHeap(), 0, sizeof(X11DRV_WND_DATA))))
203     {
204         WIN_ReleaseWndPtr( wndPtr );
205         return FALSE;
206     }
207     data->window = 0;
208     wndPtr->pDriverData = data;
209
210     if (!wndPtr->parent)
211     {
212         create_desktop( wndPtr );
213         WIN_ReleaseWndPtr( wndPtr );
214         return TRUE;
215     }
216
217     /* Create the X window (only for top-level windows, and then only */
218     /* when there's no desktop window) */
219
220     if ((root_window == DefaultRootWindow(display))
221         && (wndPtr->parent->hwndSelf == GetDesktopWindow()))
222     {
223         Window    wGroupLeader;
224         XWMHints* wm_hints;
225         XSetWindowAttributes win_attr;
226
227         /* Create "managed" windows only if a title bar or resizable */
228         /* frame is required. */
229         if (WIN_WindowNeedsWMBorder(wndPtr->dwStyle, wndPtr->dwExStyle))
230         {
231             win_attr.event_mask = ExposureMask | KeyPressMask |
232                 KeyReleaseMask | PointerMotionMask |
233                 ButtonPressMask | ButtonReleaseMask |
234                 FocusChangeMask | StructureNotifyMask;
235             win_attr.override_redirect = FALSE;
236             wndPtr->dwExStyle |= WS_EX_MANAGED;
237         }
238         else
239         {
240             win_attr.event_mask = ExposureMask | KeyPressMask |
241                 KeyReleaseMask | PointerMotionMask |
242                 ButtonPressMask | ButtonReleaseMask |
243                 FocusChangeMask;
244             win_attr.override_redirect = TRUE;
245         }
246         wndPtr->flags |= WIN_NATIVE;
247
248         win_attr.bit_gravity   = (wndPtr->clsStyle & (CS_VREDRAW | CS_HREDRAW)) ? ForgetGravity : NorthWestGravity;
249         win_attr.colormap      = X11DRV_PALETTE_PaletteXColormap;
250         win_attr.backing_store = NotUseful;
251         win_attr.save_under    = ((wndPtr->clsStyle & CS_SAVEBITS) != 0);
252         win_attr.cursor        = X11DRV_MOUSE_XCursor;
253
254         data->hWMIconBitmap = 0;
255         data->hWMIconMask = 0;
256         data->bit_gravity = win_attr.bit_gravity;
257
258         /* Zero-size X11 window hack.  X doesn't like them, and will crash */
259         /* with a BadValue unless we do something ugly like this. */
260         /* Zero size window won't be mapped  */
261         if (cx <= 0) cx = 1;
262         if (cy <= 0) cy = 1;
263
264         data->window = TSXCreateWindow( display, root_window,
265                                         x, y, cx, cy,
266                                         0, screen_depth,
267                                         InputOutput, visual,
268                                         CWEventMask | CWOverrideRedirect |
269                                         CWColormap | CWCursor | CWSaveUnder |
270                                         CWBackingStore | CWBitGravity,
271                                         &win_attr );
272
273         if(!(wGroupLeader = X11DRV_WND_GetXWindow(wndPtr)))
274         {
275             HeapFree( GetProcessHeap(), 0, data );
276             WIN_ReleaseWndPtr( wndPtr );
277             return FALSE;
278         }
279
280         /* If we are the systray, we need to be managed to be noticed by KWM */
281         if (wndPtr->dwExStyle & WS_EX_TRAYWINDOW) dock_window( data->window );
282
283         if (wndPtr->dwExStyle & WS_EX_MANAGED)
284         {
285             XClassHint *class_hints = TSXAllocClassHint();
286             XSizeHints* size_hints = TSXAllocSizeHints();
287
288             if (class_hints)
289             {
290                 class_hints->res_name = "wineManaged";
291                 class_hints->res_class = "Wine";
292                 TSXSetClassHint( display, data->window, class_hints );
293                 TSXFree (class_hints);
294             }
295
296             if (size_hints)
297             {
298                 size_hints->win_gravity = StaticGravity;
299                 size_hints->x = x;
300                 size_hints->y = y;
301                 size_hints->flags = PWinGravity|PPosition;
302
303                 if (HAS_DLGFRAME(wndPtr->dwStyle,wndPtr->dwExStyle))
304                 {
305                     size_hints->min_width = size_hints->max_width = cx;
306                     size_hints->min_height = size_hints->max_height = cy;
307                     size_hints->flags |= PMinSize | PMaxSize;
308                 }
309
310                 TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(wndPtr), 
311                                    size_hints, XA_WM_NORMAL_HINTS );
312                 TSXFree(size_hints);
313             }
314         }
315
316         if (wndPtr->owner)  /* Get window owner */
317         {
318             Window w = X11DRV_WND_FindXWindow( wndPtr->owner );
319             if (w != None)
320             {
321                 TSXSetTransientForHint( display, X11DRV_WND_GetXWindow(wndPtr), w );
322                 wGroupLeader = w;
323             }
324         }
325
326         if ((wm_hints = TSXAllocWMHints()))
327         {
328             wm_hints->flags = InputHint | StateHint | WindowGroupHint;
329             wm_hints->input = True;
330
331             if (wndPtr->dwExStyle & WS_EX_MANAGED)
332             {
333                 set_icon_hints( wndPtr, wm_hints );
334                 wm_hints->initial_state = (wndPtr->dwStyle & WS_MINIMIZE)
335                     ? IconicState : NormalState;
336             }
337             else
338                 wm_hints->initial_state = NormalState;
339             wm_hints->window_group = wGroupLeader;
340
341             TSXSetWMHints( display, X11DRV_WND_GetXWindow(wndPtr), wm_hints );
342             TSXFree(wm_hints);
343         }
344         register_window( hwnd, data->window );
345     }
346     WIN_ReleaseWndPtr( wndPtr );
347     return TRUE;
348 }
349
350
351 /***********************************************************************
352  *              DestroyWindow   (X11DRV.@)
353  */
354 BOOL X11DRV_DestroyWindow( HWND hwnd )
355 {
356     WND *wndPtr = WIN_FindWndPtr( hwnd );
357     X11DRV_WND_DATA *data = wndPtr->pDriverData;
358     Window w;
359
360     if (data && (w = data->window))
361     {
362         XEvent xe;
363         TSXDeleteContext( display, w, winContext );
364         TSXDestroyWindow( display, w );
365         while( TSXCheckWindowEvent(display, w, NoEventMask, &xe) );
366
367         data->window = None;
368         if( data->hWMIconBitmap )
369         {
370             DeleteObject( data->hWMIconBitmap );
371             data->hWMIconBitmap = 0;
372         }
373         if( data->hWMIconMask )
374         {
375             DeleteObject( data->hWMIconMask);
376             data->hWMIconMask= 0;
377         }
378     }
379     HeapFree( GetProcessHeap(), 0, data );
380     wndPtr->pDriverData = NULL;
381     WIN_ReleaseWndPtr( wndPtr );
382     return TRUE;
383 }
384
385
386 /*****************************************************************
387  *              SetParent   (X11DRV.@)
388  */
389 HWND X11DRV_SetParent( HWND hwnd, HWND parent )
390 {
391     WND *wndPtr;
392     WND *pWndParent;
393     DWORD dwStyle;
394     HWND retvalue;
395
396     if (!(wndPtr = WIN_FindWndPtr(hwnd))) return 0;
397
398     dwStyle = wndPtr->dwStyle;
399
400     pWndParent = parent ? WIN_FindWndPtr(parent) : WIN_GetDesktop();
401     if (!pWndParent)
402     {
403         WIN_ReleaseWndPtr( wndPtr );
404         return 0;
405     }
406
407     /* Windows hides the window first, then shows it again
408      * including the WM_SHOWWINDOW messages and all */
409     if (dwStyle & WS_VISIBLE) ShowWindow( hwnd, SW_HIDE );
410
411     retvalue = wndPtr->parent->hwndSelf;  /* old parent */
412     if (pWndParent != wndPtr->parent)
413     {
414         if ( X11DRV_WND_GetXWindow(wndPtr) )
415         {
416             /* Toplevel window needs to be reparented.  Used by Tk 8.0 */
417             TSXDestroyWindow( display, X11DRV_WND_GetXWindow(wndPtr) );
418             ((X11DRV_WND_DATA *) wndPtr->pDriverData)->window = None;
419         }
420         WIN_UnlinkWindow(wndPtr->hwndSelf);
421         wndPtr->parent = pWndParent;
422
423         /* Create an X counterpart for reparented top-level windows
424              * when not in the desktop mode. */
425         if (parent == GetDesktopWindow())
426         {
427             if(root_window == DefaultRootWindow(display))
428                 X11DRV_CreateWindow(wndPtr->hwndSelf);
429         }
430         else /* a child window */
431         {
432             if( !( wndPtr->dwStyle & WS_CHILD ) )
433             {
434                 if( wndPtr->wIDmenu != 0)
435                 {
436                     DestroyMenu( (HMENU) wndPtr->wIDmenu );
437                     wndPtr->wIDmenu = 0;
438                 }
439             }
440         }
441         WIN_LinkWindow(wndPtr->hwndSelf, HWND_TOP);
442     }
443     WIN_ReleaseWndPtr( pWndParent );
444     WIN_ReleaseWndPtr( wndPtr );
445
446     /* SetParent additionally needs to make hwnd the topmost window
447        in the x-order and send the expected WM_WINDOWPOSCHANGING and
448        WM_WINDOWPOSCHANGED notification messages. 
449     */
450     SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0,
451                   SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|
452                   ((dwStyle & WS_VISIBLE)?SWP_SHOWWINDOW:0));
453     /* FIXME: a WM_MOVE is also generated (in the DefWindowProc handler
454      * for WM_WINDOWPOSCHANGED) in Windows, should probably remove SWP_NOMOVE */
455
456     return retvalue;
457 }
458
459
460 /*******************************************************************
461  *              EnableWindow   (X11DRV.@)
462  */
463 BOOL X11DRV_EnableWindow( HWND hwnd, BOOL enable )
464 {
465     WND *wndPtr;
466     BOOL retvalue;
467     Window w;
468
469     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
470
471     retvalue = ((wndPtr->dwStyle & WS_DISABLED) != 0);
472
473     if (enable && (wndPtr->dwStyle & WS_DISABLED))
474     {
475         /* Enable window */
476         wndPtr->dwStyle &= ~WS_DISABLED;
477
478         if ((wndPtr->dwExStyle & WS_EX_MANAGED) && (w = X11DRV_WND_GetXWindow( wndPtr )))
479             set_wm_hint( w, InputHint, TRUE );
480
481         SendMessageA( hwnd, WM_ENABLE, TRUE, 0 );
482     }
483     else if (!enable && !(wndPtr->dwStyle & WS_DISABLED))
484     {
485         SendMessageA( wndPtr->hwndSelf, WM_CANCELMODE, 0, 0 );
486
487         /* Disable window */
488         wndPtr->dwStyle |= WS_DISABLED;
489
490         if ((wndPtr->dwExStyle & WS_EX_MANAGED) && (w = X11DRV_WND_GetXWindow( wndPtr )))
491             set_wm_hint( w, InputHint, FALSE );
492
493         if (hwnd == GetFocus())
494             SetFocus( 0 );  /* A disabled window can't have the focus */
495
496         if (hwnd == GetCapture())
497             ReleaseCapture();  /* A disabled window can't capture the mouse */
498
499         SendMessageA( hwnd, WM_ENABLE, FALSE, 0 );
500     }
501     WIN_ReleaseWndPtr(wndPtr);
502     return retvalue;
503 }
504
505
506 /*****************************************************************
507  *              SetFocus   (X11DRV.@)
508  *
509  * Set the X focus.
510  * Explicit colormap management seems to work only with OLVWM.
511  */
512 void X11DRV_SetFocus( HWND hwnd )
513 {
514     XWindowAttributes win_attr;
515     Window win;
516     WND *wndPtr = WIN_FindWndPtr( hwnd );
517     WND *w = wndPtr;
518
519     if (!wndPtr) return;
520
521     /* Only mess with the X focus if there's */
522     /* no desktop window and if the window is not managed by the WM. */
523     if (root_window != DefaultRootWindow(display)) goto done;
524
525     while (w && !((X11DRV_WND_DATA *) w->pDriverData)->window)
526         w = w->parent;
527     if (!w) w = wndPtr;
528     if (w->dwExStyle & WS_EX_MANAGED) goto done;
529
530     if (!hwnd)  /* If setting the focus to 0, uninstall the colormap */
531     {
532         if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
533             TSXUninstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
534     }
535     else if ((win = X11DRV_WND_FindXWindow(wndPtr)))
536     {
537         /* Set X focus and install colormap */
538         if (TSXGetWindowAttributes( display, win, &win_attr ) &&
539             (win_attr.map_state == IsViewable))
540         {
541             /* If window is not viewable, don't change anything */
542             TSXSetInputFocus( display, win, RevertToParent, CurrentTime );
543             if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
544                 TSXInstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
545             X11DRV_Synchronize();
546         }
547     }
548
549  done:
550     WIN_ReleaseWndPtr( wndPtr );
551 }
552
553
554 /*****************************************************************
555  *              SetWindowText   (X11DRV.@)
556  */
557 BOOL X11DRV_SetWindowText( HWND hwnd, LPCWSTR text )
558 {
559     UINT count;
560     char *buffer;
561     static UINT text_cp = (UINT)-1;
562     Window win;
563     WND *wndPtr = WIN_FindWndPtr( hwnd );
564
565     if (!wndPtr) return FALSE;
566     if ((win = X11DRV_WND_GetXWindow(wndPtr)))
567     {
568         if (text_cp == (UINT)-1)
569         {
570             text_cp = PROFILE_GetWineIniInt("x11drv", "TextCP", CP_ACP);
571             TRACE("text_cp = %u\n", text_cp);
572         }
573
574         /* allocate new buffer for window text */
575         count = WideCharToMultiByte(text_cp, 0, text, -1, NULL, 0, NULL, NULL);
576         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) )))
577         {
578             ERR("Not enough memory for window text\n");
579             WIN_ReleaseWndPtr( wndPtr );
580             return FALSE;
581         }
582         WideCharToMultiByte(text_cp, 0, text, -1, buffer, count, NULL, NULL);
583
584         TSXStoreName( display, win, buffer );
585         TSXSetIconName( display, win, buffer );
586         HeapFree( GetProcessHeap(), 0, buffer );
587     }
588     WIN_ReleaseWndPtr( wndPtr );
589     return TRUE;
590 }
591
592
593 /**********************************************************************
594  *              X11DRV_SetWindowIcon
595  *
596  * hIcon or hIconSm has changed (or is being initialised for the
597  * first time). Complete the X11 driver-specific initialisation
598  * and set the window hints.
599  *
600  * This is not entirely correct, may need to create
601  * an icon window and set the pixmap as a background
602  */
603 HICON X11DRV_SetWindowIcon( HWND hwnd, HICON icon, BOOL small )
604 {
605     WND *wndPtr = WIN_FindWndPtr( hwnd );
606     int index = small ? GCL_HICONSM : GCL_HICON;
607     HICON old;
608
609     if (!wndPtr) return 0;
610
611     old = GetClassLongW( hwnd, index );
612     SetClassLongW( hwnd, index, icon );
613
614     SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE |
615                   SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
616
617     if (wndPtr->dwExStyle & WS_EX_MANAGED)
618     {
619         Window win = X11DRV_WND_GetXWindow(wndPtr);
620         XWMHints* wm_hints = TSXGetWMHints( display, win );
621
622         if (!wm_hints) wm_hints = TSXAllocWMHints();
623         if (wm_hints)
624         {
625             set_icon_hints( wndPtr, wm_hints );
626             TSXSetWMHints( display, win, wm_hints );
627             TSXFree( wm_hints );
628         }
629     }
630
631     WIN_ReleaseWndPtr( wndPtr );
632     return old;
633 }