Store window parent as an HWND instead of a pointer.
[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 <stdlib.h>
12
13 #include "ts_xlib.h"
14 #include "ts_xutil.h"
15
16 #include "winbase.h"
17 #include "wingdi.h"
18 #include "winreg.h"
19 #include "winuser.h"
20 #include "wine/unicode.h"
21
22 #include "debugtools.h"
23 #include "x11drv.h"
24 #include "win.h"
25 #include "dce.h"
26 #include "options.h"
27
28 DEFAULT_DEBUG_CHANNEL(x11drv);
29
30 extern Pixmap X11DRV_BITMAP_Pixmap( HBITMAP );
31
32 #define HAS_DLGFRAME(style,exStyle) \
33     (((exStyle) & WS_EX_DLGMODALFRAME) || \
34      (((style) & WS_DLGFRAME) && !((style) & WS_THICKFRAME)))
35
36 /* X context to associate a hwnd to an X window */
37 XContext winContext = 0;
38
39 Atom wmProtocols = None;
40 Atom wmDeleteWindow = None;
41 Atom wmTakeFocus = None;
42 Atom dndProtocol = None;
43 Atom dndSelection = None;
44 Atom wmChangeState = None;
45 Atom kwmDockWindow = None;
46 Atom _kde_net_wm_system_tray_window_for = None; /* KDE 2 Final */
47
48
49 /***********************************************************************
50  *              is_window_managed
51  *
52  * Check if a given window should be managed
53  */
54 inline static BOOL is_window_managed( WND *win )
55 {
56     if (!Options.managed) return FALSE;
57
58     /* tray window is always managed */
59     if (win->dwExStyle & WS_EX_TRAYWINDOW) return TRUE;
60     /* child windows are not managed */
61     if (win->dwStyle & WS_CHILD) return FALSE;
62     /* tool windows are not managed */
63     if (win->dwExStyle & WS_EX_TOOLWINDOW) return FALSE;
64     /* windows with caption or thick frame are managed */
65     if ((win->dwStyle & WS_CAPTION) == WS_CAPTION) return TRUE;
66     if (win->dwStyle & WS_THICKFRAME) return TRUE;
67     /* default: not managed */
68     return FALSE;
69 }
70
71
72 /***********************************************************************
73  *              is_window_top_level
74  *
75  * Check if a given window is a top level X11 window
76  */
77 inline static BOOL is_window_top_level( WND *win )
78 {
79     return (root_window == DefaultRootWindow(gdi_display) && win->parent == GetDesktopWindow());
80 }
81
82
83 /***********************************************************************
84  *              is_client_window_mapped
85  *
86  * Check if the X client window should be mapped
87  */
88 inline static BOOL is_client_window_mapped( WND *win )
89 {
90     struct x11drv_win_data *data = win->pDriverData;
91     return !(win->dwStyle & WS_MINIMIZE) && !IsRectEmpty( &data->client_rect );
92 }
93
94
95 /***********************************************************************
96  *              get_window_attributes
97  *
98  * Fill the window attributes structure for an X window.
99  * Returned cursor must be freed by caller.
100  */
101 static int get_window_attributes( Display *display, WND *win, XSetWindowAttributes *attr )
102 {
103     BOOL is_top_level = is_window_top_level( win );
104     BOOL managed = is_top_level && is_window_managed( win );
105
106     if (managed) win->dwExStyle |= WS_EX_MANAGED;
107     else win->dwExStyle &= ~WS_EX_MANAGED;
108
109     attr->override_redirect = !managed;
110     attr->colormap          = X11DRV_PALETTE_PaletteXColormap;
111     attr->save_under        = ((win->clsStyle & CS_SAVEBITS) != 0);
112     attr->cursor            = None;
113     attr->event_mask        = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
114                                ButtonPressMask | ButtonReleaseMask);
115     if (is_window_top_level( win ))
116     {
117         attr->event_mask |= StructureNotifyMask | FocusChangeMask;
118         attr->cursor = X11DRV_GetCursor( display, GlobalLock16(GetCursor()) );
119     }
120     return (CWOverrideRedirect | CWSaveUnder | CWEventMask | CWColormap | CWCursor);
121 }
122
123
124 /***********************************************************************
125  *              sync_window_style
126  *
127  * Change the X window attributes when the window style has changed.
128  */
129 static void sync_window_style( Display *display, WND *win )
130 {
131     XSetWindowAttributes attr;
132     int mask;
133
134     wine_tsx11_lock();
135     mask = get_window_attributes( display, win, &attr );
136     XChangeWindowAttributes( display, get_whole_window(win), mask, &attr );
137     if (attr.cursor) XFreeCursor( display, attr.cursor );
138     wine_tsx11_unlock();
139 }
140
141
142 /***********************************************************************
143  *              get_window_changes
144  *
145  * fill the window changes structure
146  */
147 static int get_window_changes( XWindowChanges *changes, const RECT *old, const RECT *new )
148 {
149     int mask = 0;
150
151     if (old->right - old->left != new->right - new->left )
152     {
153         if (!(changes->width = new->right - new->left)) changes->width = 1;
154         mask |= CWWidth;
155     }
156     if (old->bottom - old->top != new->bottom - new->top)
157     {
158         if (!(changes->height = new->bottom - new->top)) changes->height = 1;
159         mask |= CWHeight;
160     }
161     if (old->left != new->left)
162     {
163         changes->x = new->left;
164         mask |= CWX;
165     }
166     if (old->top != new->top)
167     {
168         changes->y = new->top;
169         mask |= CWY;
170     }
171     return mask;
172 }
173
174
175 /***********************************************************************
176  *              create_icon_window
177  */
178 static Window create_icon_window( Display *display, WND *win )
179 {
180     struct x11drv_win_data *data = win->pDriverData;
181     XSetWindowAttributes attr;
182
183     attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
184                        ButtonPressMask | ButtonReleaseMask);
185     attr.bit_gravity = NorthWestGravity;
186     attr.backing_store = NotUseful/*WhenMapped*/;
187     attr.colormap      = X11DRV_PALETTE_PaletteXColormap; /* Needed due to our visual */
188
189     wine_tsx11_lock();
190     data->icon_window = XCreateWindow( display, root_window, 0, 0,
191                                        GetSystemMetrics( SM_CXICON ),
192                                        GetSystemMetrics( SM_CYICON ),
193                                        0, screen_depth,
194                                        InputOutput, visual,
195                                        CWEventMask | CWBitGravity | CWBackingStore | CWColormap, &attr );
196     XSaveContext( display, data->icon_window, winContext, (char *)win->hwndSelf );
197     wine_tsx11_unlock();
198
199     TRACE( "created %lx\n", data->icon_window );
200     SetPropA( win->hwndSelf, "__wine_x11_icon_window", (HANDLE)data->icon_window );
201     return data->icon_window;
202 }
203
204
205
206 /***********************************************************************
207  *              destroy_icon_window
208  */
209 inline static void destroy_icon_window( Display *display, WND *win )
210 {
211     struct x11drv_win_data *data = win->pDriverData;
212
213     if (!data->icon_window) return;
214     wine_tsx11_lock();
215     XDeleteContext( display, data->icon_window, winContext );
216     XDestroyWindow( display, data->icon_window );
217     data->icon_window = 0;
218     wine_tsx11_unlock();
219     RemovePropA( win->hwndSelf, "__wine_x11_icon_window" );
220 }
221
222
223 /***********************************************************************
224  *              set_icon_hints
225  *
226  * Set the icon wm hints
227  */
228 static void set_icon_hints( Display *display, WND *wndPtr, XWMHints *hints )
229 {
230     X11DRV_WND_DATA *data = wndPtr->pDriverData;
231     HICON hIcon = GetClassLongA( wndPtr->hwndSelf, GCL_HICON );
232
233     if (data->hWMIconBitmap) DeleteObject( data->hWMIconBitmap );
234     if (data->hWMIconMask) DeleteObject( data->hWMIconMask);
235     data->hWMIconBitmap = 0;
236     data->hWMIconMask = 0;
237
238     if (!(wndPtr->dwExStyle & WS_EX_MANAGED))
239     {
240         destroy_icon_window( display, wndPtr );
241         hints->flags &= ~(IconPixmapHint | IconMaskHint | IconWindowHint);
242     }
243     else if (!hIcon)
244     {
245         if (!data->icon_window) create_icon_window( display, wndPtr );
246         hints->icon_window = data->icon_window;
247         hints->flags = (hints->flags & ~(IconPixmapHint | IconMaskHint)) | IconWindowHint;
248     }
249     else
250     {
251         HBITMAP hbmOrig;
252         RECT rcMask;
253         BITMAP bmMask;
254         ICONINFO ii;
255         HDC hDC;
256
257         GetIconInfo(hIcon, &ii);
258
259         X11DRV_CreateBitmap(ii.hbmMask);
260         X11DRV_CreateBitmap(ii.hbmColor);
261
262         GetObjectA(ii.hbmMask, sizeof(bmMask), &bmMask);
263         rcMask.top    = 0;
264         rcMask.left   = 0;
265         rcMask.right  = bmMask.bmWidth;
266         rcMask.bottom = bmMask.bmHeight;
267
268         hDC = CreateCompatibleDC(0);
269         hbmOrig = SelectObject(hDC, ii.hbmMask);
270         InvertRect(hDC, &rcMask);
271         SelectObject(hDC, hbmOrig);
272         DeleteDC(hDC);
273
274         data->hWMIconBitmap = ii.hbmColor;
275         data->hWMIconMask = ii.hbmMask;
276
277         hints->icon_pixmap = X11DRV_BITMAP_Pixmap(data->hWMIconBitmap);
278         hints->icon_mask = X11DRV_BITMAP_Pixmap(data->hWMIconMask);
279         destroy_icon_window( display, wndPtr );
280         hints->flags = (hints->flags & ~IconWindowHint) | IconPixmapHint | IconMaskHint;
281     }
282 }
283
284
285 /***********************************************************************
286  *              set_size_hints
287  *
288  * set the window size hints
289  */
290 static void set_size_hints( Display *display, WND *win )
291 {
292     XSizeHints* size_hints;
293     struct x11drv_win_data *data = win->pDriverData;
294
295     if ((size_hints = XAllocSizeHints()))
296     {
297         size_hints->win_gravity = StaticGravity;
298         size_hints->x = data->whole_rect.left;
299         size_hints->y = data->whole_rect.top;
300         size_hints->flags = PWinGravity | PPosition;
301
302         if (HAS_DLGFRAME( win->dwStyle, win->dwExStyle ))
303         {
304             size_hints->max_width = data->whole_rect.right - data->whole_rect.left;
305             size_hints->max_height = data->whole_rect.bottom - data->whole_rect.top;
306             size_hints->min_width = size_hints->max_width;
307             size_hints->min_height = size_hints->max_height;
308             size_hints->flags |= PMinSize | PMaxSize;
309         }
310         XSetWMNormalHints( display, data->whole_window, size_hints );
311         XFree( size_hints );
312     }
313 }
314
315
316 /***********************************************************************
317  *              set_wm_hints
318  *
319  * Set the window manager hints for a newly-created window
320  */
321 static void set_wm_hints( Display *display, WND *win )
322 {
323     struct x11drv_win_data *data = win->pDriverData;
324     Window group_leader;
325     XClassHint *class_hints;
326     XWMHints* wm_hints;
327     Atom protocols[2];
328     int i;
329
330     wine_tsx11_lock();
331
332     /* wm protocols */
333     i = 0;
334     protocols[i++] = wmDeleteWindow;
335     if (wmTakeFocus) protocols[i++] = wmTakeFocus;
336     XSetWMProtocols( display, data->whole_window, protocols, i );
337
338     /* class hints */
339     if ((class_hints = XAllocClassHint()))
340     {
341         class_hints->res_name = "wine";
342         class_hints->res_class = "Wine";
343         XSetClassHint( display, data->whole_window, class_hints );
344         XFree( class_hints );
345     }
346
347     /* transient for hint */
348     if (win->owner)
349     {
350         Window owner_win = X11DRV_get_whole_window( win->owner );
351         XSetTransientForHint( display, data->whole_window, owner_win );
352         group_leader = owner_win;
353     }
354     else group_leader = data->whole_window;
355
356     /* size hints */
357     set_size_hints( display, win );
358
359     /* systray properties (KDE only for now) */
360     if (win->dwExStyle & WS_EX_TRAYWINDOW)
361     {
362         int val = 1;
363         if (kwmDockWindow != None)
364             TSXChangeProperty( display, data->whole_window, kwmDockWindow, kwmDockWindow,
365                                32, PropModeReplace, (char*)&val, 1 );
366         if (_kde_net_wm_system_tray_window_for != None)
367             TSXChangeProperty( display, data->whole_window, _kde_net_wm_system_tray_window_for,
368                                XA_WINDOW, 32, PropModeReplace, (char*)&data->whole_window, 1 );
369     }
370
371     wine_tsx11_unlock();
372
373     /* wm hints */
374     if ((wm_hints = TSXAllocWMHints()))
375     {
376         wm_hints->flags = InputHint | StateHint | WindowGroupHint;
377         /* use globally active model if take focus is supported,
378          * passive model otherwise (cf. ICCCM) */
379         wm_hints->input = !wmTakeFocus;
380
381         set_icon_hints( display, win, wm_hints );
382
383         wm_hints->initial_state = (win->dwStyle & WS_MINIMIZE) ? IconicState : NormalState;
384         wm_hints->window_group = group_leader;
385
386         wine_tsx11_lock();
387         XSetWMHints( display, data->whole_window, wm_hints );
388         XFree(wm_hints);
389         wine_tsx11_unlock();
390     }
391 }
392
393
394 /***********************************************************************
395  *              X11DRV_set_iconic_state
396  *
397  * Set the X11 iconic state according to the window style.
398  */
399 void X11DRV_set_iconic_state( WND *win )
400 {
401     Display *display = thread_display();
402     struct x11drv_win_data *data = win->pDriverData;
403     XWMHints* wm_hints;
404     BOOL iconic = IsIconic( win->hwndSelf );
405
406     wine_tsx11_lock();
407
408     if (iconic) XUnmapWindow( display, data->client_window );
409     else if (is_client_window_mapped( win )) XMapWindow( display, data->client_window );
410
411     if (!(wm_hints = XGetWMHints( display, data->whole_window ))) wm_hints = XAllocWMHints();
412     wm_hints->flags |= StateHint | IconPositionHint;
413     wm_hints->initial_state = iconic ? IconicState : NormalState;
414     wm_hints->icon_x = win->rectWindow.left;
415     wm_hints->icon_y = win->rectWindow.top;
416     XSetWMHints( display, data->whole_window, wm_hints );
417
418     if (win->dwStyle & WS_VISIBLE)
419     {
420         if (iconic)
421             XIconifyWindow( display, data->whole_window, DefaultScreen(display) );
422         else
423             if (!IsRectEmpty( &win->rectWindow )) XMapWindow( display, data->whole_window );
424     }
425
426     XFree(wm_hints);
427     wine_tsx11_unlock();
428 }
429
430
431 /***********************************************************************
432  *              X11DRV_window_to_X_rect
433  *
434  * Convert a rect from client to X window coordinates
435  */
436 void X11DRV_window_to_X_rect( WND *win, RECT *rect )
437 {
438     RECT rc;
439
440     if (!(win->dwExStyle & WS_EX_MANAGED)) return;
441     if (IsRectEmpty( rect )) return;
442
443     rc.top = rc.bottom = rc.left = rc.right = 0;
444
445     AdjustWindowRectEx( &rc, win->dwStyle & ~(WS_HSCROLL|WS_VSCROLL), FALSE, win->dwExStyle );
446
447     rect->left   -= rc.left;
448     rect->right  -= rc.right;
449     rect->top    -= rc.top;
450     rect->bottom -= rc.bottom;
451     if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
452     if (rect->left >= rect->right) rect->right = rect->left + 1;
453 }
454
455
456 /***********************************************************************
457  *              X11DRV_X_to_window_rect
458  *
459  * Opposite of X11DRV_window_to_X_rect
460  */
461 void X11DRV_X_to_window_rect( WND *win, RECT *rect )
462 {
463     if (!(win->dwExStyle & WS_EX_MANAGED)) return;
464     if (IsRectEmpty( rect )) return;
465
466     AdjustWindowRectEx( rect, win->dwStyle & ~(WS_HSCROLL|WS_VSCROLL), FALSE, win->dwExStyle );
467
468     if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
469     if (rect->left >= rect->right) rect->right = rect->left + 1;
470 }
471
472
473 /***********************************************************************
474  *              X11DRV_sync_whole_window_position
475  *
476  * Synchronize the X whole window position with the Windows one
477  */
478 int X11DRV_sync_whole_window_position( Display *display, WND *win, int zorder )
479 {
480     XWindowChanges changes;
481     int mask;
482     struct x11drv_win_data *data = win->pDriverData;
483     RECT whole_rect = win->rectWindow;
484
485     X11DRV_window_to_X_rect( win, &whole_rect );
486     mask = get_window_changes( &changes, &data->whole_rect, &whole_rect );
487
488     if (zorder)
489     {
490         /* find window that this one must be after */
491         HWND prev = GetWindow( win->hwndSelf, GW_HWNDPREV );
492         while (prev && !(GetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE))
493             prev = GetWindow( prev, GW_HWNDPREV );
494         if (!prev)  /* top child */
495         {
496             changes.stack_mode = Above;
497             mask |= CWStackMode;
498         }
499         else
500         {
501             changes.stack_mode = Below;
502             changes.sibling = X11DRV_get_whole_window(prev);
503             mask |= CWStackMode | CWSibling;
504         }
505     }
506
507     data->whole_rect = whole_rect;
508
509     if (mask)
510     {
511         TRACE( "setting win %lx pos %d,%d,%dx%d after %lx changes=%x\n",
512                data->whole_window, whole_rect.left, whole_rect.top,
513                whole_rect.right - whole_rect.left, whole_rect.bottom - whole_rect.top,
514                changes.sibling, mask );
515         wine_tsx11_lock();
516         XSync( gdi_display, False );  /* flush graphics operations before moving the window */
517         if (is_window_top_level( win ))
518         {
519             if (mask & (CWWidth|CWHeight)) set_size_hints( display, win );
520             XReconfigureWMWindow( display, data->whole_window,
521                                   DefaultScreen(display), mask, &changes );
522         }
523         else XConfigureWindow( display, data->whole_window, mask, &changes );
524         wine_tsx11_unlock();
525     }
526     return mask;
527 }
528
529
530 /***********************************************************************
531  *              X11DRV_sync_client_window_position
532  *
533  * Synchronize the X client window position with the Windows one
534  */
535 int X11DRV_sync_client_window_position( Display *display, WND *win )
536 {
537     XWindowChanges changes;
538     int mask;
539     struct x11drv_win_data *data = win->pDriverData;
540     RECT client_rect = win->rectClient;
541
542     OffsetRect( &client_rect, -data->whole_rect.left, -data->whole_rect.top );
543
544     if ((mask = get_window_changes( &changes, &data->client_rect, &client_rect )))
545     {
546         BOOL was_mapped = is_client_window_mapped( win );
547
548         TRACE( "setting win %lx pos %d,%d,%dx%d (was %d,%d,%dx%d) after %lx changes=%x\n",
549                data->client_window, client_rect.left, client_rect.top,
550                client_rect.right - client_rect.left, client_rect.bottom - client_rect.top,
551                data->client_rect.left, data->client_rect.top,
552                data->client_rect.right - data->client_rect.left,
553                data->client_rect.bottom - data->client_rect.top,
554                changes.sibling, mask );
555         data->client_rect = client_rect;
556         wine_tsx11_lock();
557         XSync( gdi_display, False );  /* flush graphics operations before moving the window */
558         if (was_mapped && !is_client_window_mapped( win ))
559             XUnmapWindow( display, data->client_window );
560         XConfigureWindow( display, data->client_window, mask, &changes );
561         if (!was_mapped && is_client_window_mapped( win ))
562             XMapWindow( display, data->client_window );
563         wine_tsx11_unlock();
564     }
565     return mask;
566 }
567
568
569 /***********************************************************************
570  *              X11DRV_register_window
571  *
572  * Associate an X window to a HWND.
573  */
574 void X11DRV_register_window( Display *display, HWND hwnd, struct x11drv_win_data *data )
575 {
576     wine_tsx11_lock();
577     XSaveContext( display, data->whole_window, winContext, (char *)hwnd );
578     XSaveContext( display, data->client_window, winContext, (char *)hwnd );
579     wine_tsx11_unlock();
580 }
581
582
583 /**********************************************************************
584  *              create_desktop
585  */
586 static void create_desktop( Display *display, WND *wndPtr, CREATESTRUCTA *cs )
587 {
588     X11DRV_WND_DATA *data = wndPtr->pDriverData;
589
590     wine_tsx11_lock();
591     winContext     = XUniqueContext();
592     wmProtocols    = XInternAtom( display, "WM_PROTOCOLS", False );
593     wmDeleteWindow = XInternAtom( display, "WM_DELETE_WINDOW", False );
594 /*    wmTakeFocus    = XInternAtom( display, "WM_TAKE_FOCUS", False );*/
595     wmTakeFocus = 0;  /* not yet */
596     dndProtocol = XInternAtom( display, "DndProtocol" , False );
597     dndSelection = XInternAtom( display, "DndSelection" , False );
598     wmChangeState = XInternAtom (display, "WM_CHANGE_STATE", False);
599     kwmDockWindow = XInternAtom( display, "KWM_DOCKWINDOW", False );
600     _kde_net_wm_system_tray_window_for = XInternAtom( display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False );
601     wine_tsx11_unlock();
602
603     data->whole_window = data->client_window = root_window;
604     data->whole_rect = data->client_rect = wndPtr->rectWindow;
605
606     SetPropA( wndPtr->hwndSelf, "__wine_x11_whole_window", (HANDLE)root_window );
607     SetPropA( wndPtr->hwndSelf, "__wine_x11_client_window", (HANDLE)root_window );
608     SetPropA( wndPtr->hwndSelf, "__wine_x11_visual_id", (HANDLE)XVisualIDFromVisual(visual) );
609
610     SendMessageW( wndPtr->hwndSelf, WM_NCCREATE, 0, (LPARAM)cs );
611     if (root_window != DefaultRootWindow(display)) X11DRV_create_desktop_thread();
612 }
613
614
615 /**********************************************************************
616  *              create_whole_window
617  *
618  * Create the whole X window for a given window
619  */
620 static Window create_whole_window( Display *display, WND *win )
621 {
622     struct x11drv_win_data *data = win->pDriverData;
623     int cx, cy, mask;
624     XSetWindowAttributes attr;
625     Window parent;
626     RECT rect;
627     BOOL is_top_level = is_window_top_level( win );
628
629     rect = win->rectWindow;
630     X11DRV_window_to_X_rect( win, &rect );
631
632     if (!(cx = rect.right - rect.left)) cx = 1;
633     if (!(cy = rect.bottom - rect.top)) cy = 1;
634
635     parent = X11DRV_get_client_window( win->parent );
636
637     wine_tsx11_lock();
638
639     mask = get_window_attributes( display, win, &attr );
640
641     /* set the attributes that don't change over the lifetime of the window */
642     attr.bit_gravity       = ForgetGravity;
643     attr.win_gravity       = NorthWestGravity;
644     attr.backing_store     = NotUseful/*WhenMapped*/;
645     mask |= CWBitGravity | CWWinGravity | CWBackingStore;
646
647     data->whole_rect = rect;
648     data->whole_window = XCreateWindow( display, parent, rect.left, rect.top, cx, cy,
649                                         0, screen_depth, InputOutput, visual,
650                                         mask, &attr );
651     if (attr.cursor) XFreeCursor( display, attr.cursor );
652
653     if (!data->whole_window)
654     {
655         wine_tsx11_unlock();
656         return 0;
657     }
658
659     /* non-maximized child must be at bottom of Z order */
660     if ((win->dwStyle & (WS_CHILD|WS_MAXIMIZE)) == WS_CHILD)
661     {
662         XWindowChanges changes;
663         changes.stack_mode = Below;
664         XConfigureWindow( display, data->whole_window, CWStackMode, &changes );
665     }
666
667     wine_tsx11_unlock();
668
669     if (is_top_level) set_wm_hints( display, win );
670
671     return data->whole_window;
672 }
673
674
675 /**********************************************************************
676  *              create_client_window
677  *
678  * Create the client window for a given window
679  */
680 static Window create_client_window( Display *display, WND *win )
681 {
682     struct x11drv_win_data *data = win->pDriverData;
683     RECT rect = data->whole_rect;
684     XSetWindowAttributes attr;
685
686     OffsetRect( &rect, -data->whole_rect.left, -data->whole_rect.top );
687     data->client_rect = rect;
688
689     attr.event_mask = (ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask |
690                        ButtonPressMask | ButtonReleaseMask);
691     attr.bit_gravity = (win->clsStyle & (CS_VREDRAW | CS_HREDRAW)) ?
692                        ForgetGravity : NorthWestGravity;
693     attr.backing_store = NotUseful/*WhenMapped*/;
694
695     wine_tsx11_lock();
696     data->client_window = XCreateWindow( display, data->whole_window, 0, 0,
697                                          max( rect.right - rect.left, 1 ),
698                                          max( rect.bottom - rect.top, 1 ),
699                                          0, screen_depth,
700                                          InputOutput, visual,
701                                          CWEventMask | CWBitGravity | CWBackingStore, &attr );
702     if (data->client_window && is_client_window_mapped( win ))
703         XMapWindow( display, data->client_window );
704     wine_tsx11_unlock();
705     return data->client_window;
706 }
707
708
709 /*****************************************************************
710  *              SetWindowText   (X11DRV.@)
711  */
712 BOOL X11DRV_SetWindowText( HWND hwnd, LPCWSTR text )
713 {
714     Display *display = thread_display();
715     UINT count;
716     char *buffer;
717     char *utf8_buffer;
718     static UINT text_cp = (UINT)-1;
719     Window win;
720     WND *wndPtr = WIN_FindWndPtr( hwnd );
721
722     if (!wndPtr) return FALSE;
723     if ((win = get_whole_window(wndPtr)))
724     {
725         if (text_cp == (UINT)-1)
726         {
727             HKEY hkey;
728             /* default value */
729             text_cp = CP_ACP;
730             if(!RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\x11drv", &hkey))
731             {
732                 char buffer[20];
733                 DWORD type, count = sizeof(buffer);
734                 if(!RegQueryValueExA(hkey, "TextCP", 0, &type, buffer, &count))
735                     text_cp = atoi(buffer);
736                 RegCloseKey(hkey);
737             }
738             TRACE("text_cp = %u\n", text_cp);
739         }
740
741         /* allocate new buffer for window text */
742         count = WideCharToMultiByte(text_cp, 0, text, -1, NULL, 0, NULL, NULL);
743         if (!(buffer = HeapAlloc( GetProcessHeap(), 0, count )))
744         {
745             ERR("Not enough memory for window text\n");
746             WIN_ReleaseWndPtr( wndPtr );
747             return FALSE;
748         }
749         WideCharToMultiByte(text_cp, 0, text, -1, buffer, count, NULL, NULL);
750
751         count = WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), NULL, 0, NULL, NULL);
752         if (!(utf8_buffer = HeapAlloc( GetProcessHeap(), 0, count )))
753         {
754             ERR("Not enough memory for window text in UTF-8\n");
755             WIN_ReleaseWndPtr( wndPtr );
756             return FALSE;
757         }
758         WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), utf8_buffer, count, NULL, NULL);
759
760         wine_tsx11_lock();
761         XStoreName( display, win, buffer );
762         XSetIconName( display, win, buffer );
763         /*
764         Implements a NET_WM UTF-8 title. It should be without a trailing \0,
765         according to the standard
766         ( http://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text ).
767         */
768         XChangeProperty( display, win,
769             XInternAtom(display, "_NET_WM_NAME", False),
770             XInternAtom(display, "UTF8_STRING", False),
771             8, PropModeReplace, (unsigned char *) utf8_buffer,
772             count);
773         wine_tsx11_unlock();
774
775         HeapFree( GetProcessHeap(), 0, utf8_buffer );
776         HeapFree( GetProcessHeap(), 0, buffer );
777     }
778     WIN_ReleaseWndPtr( wndPtr );
779     return TRUE;
780 }
781
782
783 /***********************************************************************
784  *              DestroyWindow   (X11DRV.@)
785  */
786 BOOL X11DRV_DestroyWindow( HWND hwnd )
787 {
788     Display *display = thread_display();
789     WND *wndPtr = WIN_FindWndPtr( hwnd );
790     X11DRV_WND_DATA *data = wndPtr->pDriverData;
791
792     if (!data) goto done;
793
794     if (data->whole_window)
795     {
796         TRACE( "win %x xwin %lx/%lx\n", hwnd, data->whole_window, data->client_window );
797         wine_tsx11_lock();
798         XSync( gdi_display, False );  /* flush any reference to this drawable in GDI queue */
799         XDeleteContext( display, data->whole_window, winContext );
800         XDeleteContext( display, data->client_window, winContext );
801         XDestroyWindow( display, data->whole_window );  /* this destroys client too */
802         destroy_icon_window( display, wndPtr );
803         wine_tsx11_unlock();
804     }
805
806     if (data->hWMIconBitmap) DeleteObject( data->hWMIconBitmap );
807     if (data->hWMIconMask) DeleteObject( data->hWMIconMask);
808     HeapFree( GetProcessHeap(), 0, data );
809     wndPtr->pDriverData = NULL;
810  done:
811     WIN_ReleaseWndPtr( wndPtr );
812     return TRUE;
813 }
814
815
816 /**********************************************************************
817  *              CreateWindow   (X11DRV.@)
818  */
819 BOOL X11DRV_CreateWindow( HWND hwnd, CREATESTRUCTA *cs, BOOL unicode )
820 {
821     Display *display = thread_display();
822     WND *wndPtr;
823     struct x11drv_win_data *data;
824     RECT rect;
825     BOOL ret = FALSE;
826
827     if (!(data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data)))) return FALSE;
828     data->whole_window  = 0;
829     data->client_window = 0;
830     data->icon_window   = 0;
831     data->hWMIconBitmap = 0;
832     data->hWMIconMask   = 0;
833
834     wndPtr = WIN_FindWndPtr( hwnd );
835     wndPtr->pDriverData = data;
836
837     if (!wndPtr->parent)
838     {
839         create_desktop( display, wndPtr, cs );
840         WIN_ReleaseWndPtr( wndPtr );
841         return TRUE;
842     }
843
844     if (!create_whole_window( display, wndPtr )) goto failed;
845     if (!create_client_window( display, wndPtr )) goto failed;
846     TSXSync( display, False );
847
848     WIN_ReleaseWndPtr( wndPtr );
849
850     SetPropA( hwnd, "__wine_x11_whole_window", (HANDLE)data->whole_window );
851     SetPropA( hwnd, "__wine_x11_client_window", (HANDLE)data->client_window );
852
853     /* send WM_NCCREATE */
854     TRACE( "hwnd %x cs %d,%d %dx%d\n", hwnd, cs->x, cs->y, cs->cx, cs->cy );
855     if (unicode)
856         ret = SendMessageW( hwnd, WM_NCCREATE, 0, (LPARAM)cs );
857     else
858         ret = SendMessageA( hwnd, WM_NCCREATE, 0, (LPARAM)cs );
859     if (!ret)
860     {
861         X11DRV_DestroyWindow( hwnd );
862         return FALSE;
863     }
864
865     if (!(wndPtr = WIN_FindWndPtr(hwnd))) return FALSE;
866
867     sync_window_style( display, wndPtr );
868
869     /* send WM_NCCALCSIZE */
870     rect = wndPtr->rectWindow;
871     SendMessageW( hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rect );
872     if (rect.left > rect.right || rect.top > rect.bottom) rect = wndPtr->rectWindow;
873     wndPtr->rectClient = rect;
874     X11DRV_sync_client_window_position( display, wndPtr );
875     X11DRV_register_window( display, hwnd, data );
876
877     TRACE( "win %x window %d,%d,%d,%d client %d,%d,%d,%d whole %d,%d,%d,%d X client %d,%d,%d,%d xwin %x/%x\n",
878            hwnd, wndPtr->rectWindow.left, wndPtr->rectWindow.top,
879            wndPtr->rectWindow.right, wndPtr->rectWindow.bottom,
880            wndPtr->rectClient.left, wndPtr->rectClient.top,
881            wndPtr->rectClient.right, wndPtr->rectClient.bottom,
882            data->whole_rect.left, data->whole_rect.top,
883            data->whole_rect.right, data->whole_rect.bottom,
884            data->client_rect.left, data->client_rect.top,
885            data->client_rect.right, data->client_rect.bottom,
886            (unsigned int)data->whole_window, (unsigned int)data->client_window );
887
888     if ((wndPtr->dwStyle & (WS_CHILD|WS_MAXIMIZE)) == WS_CHILD)
889         WIN_LinkWindow( hwnd, wndPtr->parent, HWND_BOTTOM );
890     else
891         WIN_LinkWindow( hwnd, wndPtr->parent, HWND_TOP );
892
893     WIN_ReleaseWndPtr( wndPtr );
894
895     if (unicode)
896         ret = (SendMessageW( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
897     else
898         ret = (SendMessageA( hwnd, WM_CREATE, 0, (LPARAM)cs ) != -1);
899
900     if (!ret)
901     {
902         WIN_UnlinkWindow( hwnd );
903         X11DRV_DestroyWindow( hwnd );
904         return FALSE;
905     }
906
907     /* Send the size messages */
908
909     if (!(wndPtr = WIN_FindWndPtr(hwnd))) return FALSE;
910     if (!(wndPtr->flags & WIN_NEED_SIZE))
911     {
912         /* send it anyway */
913         if (((wndPtr->rectClient.right-wndPtr->rectClient.left) <0)
914             ||((wndPtr->rectClient.bottom-wndPtr->rectClient.top)<0))
915             WARN("sending bogus WM_SIZE message 0x%08lx\n",
916                  MAKELONG(wndPtr->rectClient.right-wndPtr->rectClient.left,
917                           wndPtr->rectClient.bottom-wndPtr->rectClient.top));
918         SendMessageW( hwnd, WM_SIZE, SIZE_RESTORED,
919                       MAKELONG(wndPtr->rectClient.right-wndPtr->rectClient.left,
920                                wndPtr->rectClient.bottom-wndPtr->rectClient.top));
921         SendMessageW( hwnd, WM_MOVE, 0,
922                       MAKELONG( wndPtr->rectClient.left, wndPtr->rectClient.top ) );
923     }
924
925     /* Show the window, maximizing or minimizing if needed */
926
927     if (wndPtr->dwStyle & (WS_MINIMIZE | WS_MAXIMIZE))
928     {
929         extern UINT WINPOS_MinMaximize( HWND hwnd, UINT cmd, LPRECT rect ); /*FIXME*/
930
931         RECT newPos;
932         UINT swFlag = (wndPtr->dwStyle & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
933         wndPtr->dwStyle &= ~(WS_MAXIMIZE | WS_MINIMIZE);
934         WINPOS_MinMaximize( hwnd, swFlag, &newPos );
935         swFlag = ((wndPtr->dwStyle & WS_CHILD) || GetActiveWindow())
936             ? SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED
937             : SWP_NOZORDER | SWP_FRAMECHANGED;
938         SetWindowPos( hwnd, 0, newPos.left, newPos.top,
939                       newPos.right, newPos.bottom, swFlag );
940     }
941
942     WIN_ReleaseWndPtr( wndPtr );
943     return TRUE;
944
945
946  failed:
947     X11DRV_DestroyWindow( wndPtr->hwndSelf );
948     WIN_ReleaseWndPtr( wndPtr );
949     return FALSE;
950 }
951
952
953 /***********************************************************************
954  *              X11DRV_get_client_window
955  *
956  * Return the X window associated with the client area of a window
957  */
958 Window X11DRV_get_client_window( HWND hwnd )
959 {
960     Window ret = 0;
961     WND *win = WIN_FindWndPtr( hwnd );
962     if (win)
963     {
964         struct x11drv_win_data *data = win->pDriverData;
965         ret = data->client_window;
966         WIN_ReleaseWndPtr( win );
967     }
968     return ret;
969 }
970
971
972 /***********************************************************************
973  *              X11DRV_get_whole_window
974  *
975  * Return the X window associated with the full area of a window
976  */
977 Window X11DRV_get_whole_window( HWND hwnd )
978 {
979     Window ret = 0;
980     WND *win = WIN_FindWndPtr( hwnd );
981     if (win)
982     {
983         struct x11drv_win_data *data = win->pDriverData;
984         ret = data->whole_window;
985         WIN_ReleaseWndPtr( win );
986     }
987     return ret;
988 }
989
990
991 /*****************************************************************
992  *              SetParent   (X11DRV.@)
993  */
994 HWND X11DRV_SetParent( HWND hwnd, HWND parent )
995 {
996     Display *display = thread_display();
997     WND *wndPtr;
998     DWORD dwStyle;
999     HWND retvalue;
1000
1001     if (!(wndPtr = WIN_FindWndPtr(hwnd))) return 0;
1002
1003     dwStyle = wndPtr->dwStyle;
1004
1005     if (!parent) parent = GetDesktopWindow();
1006
1007     /* Windows hides the window first, then shows it again
1008      * including the WM_SHOWWINDOW messages and all */
1009     if (dwStyle & WS_VISIBLE) ShowWindow( hwnd, SW_HIDE );
1010
1011     retvalue = wndPtr->parent;  /* old parent */
1012     if (parent != retvalue)
1013     {
1014         struct x11drv_win_data *data = wndPtr->pDriverData;
1015
1016         WIN_LinkWindow( hwnd, parent, HWND_TOP );
1017
1018         if (parent != GetDesktopWindow()) /* a child window */
1019         {
1020             if (!(dwStyle & WS_CHILD))
1021             {
1022                 HMENU menu = (HMENU)SetWindowLongW( hwnd, GWL_ID, 0 );
1023                 if (menu) DestroyMenu( menu );
1024             }
1025         }
1026
1027         if (is_window_top_level( wndPtr )) set_wm_hints( display, wndPtr );
1028         wine_tsx11_lock();
1029         sync_window_style( display, wndPtr );
1030         XReparentWindow( display, data->whole_window, X11DRV_get_client_window(parent),
1031                          data->whole_rect.left, data->whole_rect.top );
1032         wine_tsx11_unlock();
1033     }
1034     WIN_ReleaseWndPtr( wndPtr );
1035
1036     /* SetParent additionally needs to make hwnd the topmost window
1037        in the x-order and send the expected WM_WINDOWPOSCHANGING and
1038        WM_WINDOWPOSCHANGED notification messages. 
1039     */
1040     SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1041                   SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|
1042                   ((dwStyle & WS_VISIBLE)?SWP_SHOWWINDOW:0));
1043     /* FIXME: a WM_MOVE is also generated (in the DefWindowProc handler
1044      * for WM_WINDOWPOSCHANGED) in Windows, should probably remove SWP_NOMOVE */
1045
1046     return retvalue;
1047 }
1048
1049
1050 /*******************************************************************
1051  *              EnableWindow   (X11DRV.@)
1052  */
1053 BOOL X11DRV_EnableWindow( HWND hwnd, BOOL enable )
1054 {
1055     Display *display = thread_display();
1056     XWMHints *wm_hints;
1057     WND *wndPtr;
1058     BOOL retvalue;
1059
1060     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return FALSE;
1061     hwnd = wndPtr->hwndSelf;  /* make it a full handle */
1062
1063     retvalue = ((wndPtr->dwStyle & WS_DISABLED) != 0);
1064
1065     if (enable && (wndPtr->dwStyle & WS_DISABLED))
1066     {
1067         /* Enable window */
1068         wndPtr->dwStyle &= ~WS_DISABLED;
1069
1070         if (wndPtr->dwExStyle & WS_EX_MANAGED)
1071         {
1072             wine_tsx11_lock();
1073             if (!(wm_hints = XGetWMHints( display, get_whole_window(wndPtr) )))
1074                 wm_hints = XAllocWMHints();
1075             if (wm_hints)
1076             {
1077                 wm_hints->flags |= InputHint;
1078                 wm_hints->input = TRUE;
1079                 XSetWMHints( display, get_whole_window(wndPtr), wm_hints );
1080                 XFree(wm_hints);
1081             }
1082             wine_tsx11_unlock();
1083         }
1084
1085         SendMessageA( hwnd, WM_ENABLE, TRUE, 0 );
1086     }
1087     else if (!enable && !(wndPtr->dwStyle & WS_DISABLED))
1088     {
1089         SendMessageA( wndPtr->hwndSelf, WM_CANCELMODE, 0, 0 );
1090
1091         /* Disable window */
1092         wndPtr->dwStyle |= WS_DISABLED;
1093
1094         if (wndPtr->dwExStyle & WS_EX_MANAGED)
1095         {
1096             wine_tsx11_lock();
1097             if (!(wm_hints = XGetWMHints( display, get_whole_window(wndPtr) )))
1098                 wm_hints = XAllocWMHints();
1099             if (wm_hints)
1100             {
1101                 wm_hints->flags |= InputHint;
1102                 wm_hints->input = FALSE;
1103                 XSetWMHints( display, get_whole_window(wndPtr), wm_hints );
1104                 XFree(wm_hints);
1105             }
1106             wine_tsx11_unlock();
1107         }
1108
1109         if (hwnd == GetFocus())
1110             SetFocus( 0 );  /* A disabled window can't have the focus */
1111
1112         if (hwnd == GetCapture())
1113             ReleaseCapture();  /* A disabled window can't capture the mouse */
1114
1115         SendMessageA( hwnd, WM_ENABLE, FALSE, 0 );
1116     }
1117     WIN_ReleaseWndPtr(wndPtr);
1118     return retvalue;
1119 }
1120
1121
1122 /*****************************************************************
1123  *              SetFocus   (X11DRV.@)
1124  *
1125  * Set the X focus.
1126  * Explicit colormap management seems to work only with OLVWM.
1127  */
1128 void X11DRV_SetFocus( HWND hwnd )
1129 {
1130     Display *display = thread_display();
1131     XWindowAttributes win_attr;
1132     Window win;
1133
1134     /* Only mess with the X focus if there's */
1135     /* no desktop window and if the window is not managed by the WM. */
1136     if (root_window != DefaultRootWindow(display)) return;
1137
1138     if (!hwnd)  /* If setting the focus to 0, uninstall the colormap */
1139     {
1140         if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
1141             TSXUninstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
1142         return;
1143     }
1144
1145     hwnd = GetAncestor( hwnd, GA_ROOT );
1146     if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED) return;
1147     if (!(win = X11DRV_get_whole_window( hwnd ))) return;
1148
1149     /* Set X focus and install colormap */
1150     wine_tsx11_lock();
1151     if (XGetWindowAttributes( display, win, &win_attr ) &&
1152         (win_attr.map_state == IsViewable))
1153     {
1154         /* If window is not viewable, don't change anything */
1155
1156         /* we must not use CurrentTime (ICCCM), so try to use last message time instead */
1157         /* FIXME: this is not entirely correct */
1158         XSetInputFocus( display, win, RevertToParent,
1159                         /*CurrentTime*/ GetMessageTime() + X11DRV_server_startticks );
1160         if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
1161             XInstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
1162     }
1163     wine_tsx11_unlock();
1164 }
1165
1166
1167 /**********************************************************************
1168  *              SetWindowIcon (X11DRV.@)
1169  *
1170  * hIcon or hIconSm has changed (or is being initialised for the
1171  * first time). Complete the X11 driver-specific initialisation
1172  * and set the window hints.
1173  *
1174  * This is not entirely correct, may need to create
1175  * an icon window and set the pixmap as a background
1176  */
1177 HICON X11DRV_SetWindowIcon( HWND hwnd, HICON icon, BOOL small )
1178 {
1179     Display *display = thread_display();
1180     WND *wndPtr = WIN_FindWndPtr( hwnd );
1181     int index = small ? GCL_HICONSM : GCL_HICON;
1182     HICON old;
1183
1184     if (!wndPtr) return 0;
1185
1186     old = GetClassLongW( hwnd, index );
1187     SetClassLongW( hwnd, index, icon );
1188
1189     SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE |
1190                   SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
1191
1192     if (wndPtr->dwExStyle & WS_EX_MANAGED)
1193     {
1194         Window win = get_whole_window(wndPtr);
1195         XWMHints* wm_hints = TSXGetWMHints( display, win );
1196
1197         if (!wm_hints) wm_hints = TSXAllocWMHints();
1198         if (wm_hints)
1199         {
1200             set_icon_hints( display, wndPtr, wm_hints );
1201             TSXSetWMHints( display, win, wm_hints );
1202             TSXFree( wm_hints );
1203         }
1204     }
1205
1206     WIN_ReleaseWndPtr( wndPtr );
1207     return old;
1208 }