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