winex11: Make sure to flush painting operations before moving a window.
[wine] / dlls / winex11.drv / winpos.c
1 /*
2  * Window position related functions.
3  *
4  * Copyright 1993, 1994, 1995, 2001 Alexandre Julliard
5  * Copyright 1995, 1996, 1999 Alex Korobka
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winerror.h"
33 #include "wine/wingdi16.h"
34
35 #include "x11drv.h"
36
37 #include "wine/server.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
41
42 #define SWP_AGG_NOPOSCHANGE \
43     (SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
44
45 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
46 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
47 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
48 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
49 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
50 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
51 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
52 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
53 #define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
54 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
55 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
56
57 #define _NET_WM_STATE_REMOVE  0
58 #define _NET_WM_STATE_ADD     1
59 #define _NET_WM_STATE_TOGGLE  2
60
61 static const char managed_prop[] = "__wine_x11_managed";
62
63 /***********************************************************************
64  *           X11DRV_Expose
65  */
66 void X11DRV_Expose( HWND hwnd, XEvent *xev )
67 {
68     XExposeEvent *event = &xev->xexpose;
69     RECT rect;
70     struct x11drv_win_data *data;
71     int flags = RDW_INVALIDATE | RDW_ERASE;
72
73     TRACE( "win %p (%lx) %d,%d %dx%d\n",
74            hwnd, event->window, event->x, event->y, event->width, event->height );
75
76     if (!(data = X11DRV_get_win_data( hwnd ))) return;
77
78     if (event->window == data->whole_window)
79     {
80         rect.left = data->whole_rect.left + event->x;
81         rect.top  = data->whole_rect.top + event->y;
82         flags |= RDW_FRAME;
83     }
84     else
85     {
86         rect.left = data->client_rect.left + event->x;
87         rect.top  = data->client_rect.top + event->y;
88     }
89     rect.right  = rect.left + event->width;
90     rect.bottom = rect.top + event->height;
91
92     if (event->window != root_window)
93     {
94         SERVER_START_REQ( update_window_zorder )
95         {
96             req->window      = hwnd;
97             req->rect.left   = rect.left;
98             req->rect.top    = rect.top;
99             req->rect.right  = rect.right;
100             req->rect.bottom = rect.bottom;
101             wine_server_call( req );
102         }
103         SERVER_END_REQ;
104
105         /* make position relative to client area instead of parent */
106         OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
107         flags |= RDW_ALLCHILDREN;
108     }
109
110     RedrawWindow( hwnd, &rect, 0, flags );
111 }
112
113
114 /***********************************************************************
115  *     update_net_wm_states
116  */
117 static void update_net_wm_states( Display *display, struct x11drv_win_data *data )
118 {
119     static const unsigned int state_atoms[NB_NET_WM_STATES] =
120     {
121         XATOM__NET_WM_STATE_FULLSCREEN,
122         XATOM__NET_WM_STATE_ABOVE,
123         XATOM__NET_WM_STATE_MAXIMIZED_VERT,
124         XATOM__NET_WM_STATE_SKIP_PAGER,
125         XATOM__NET_WM_STATE_SKIP_TASKBAR
126     };
127
128     DWORD i, style, ex_style, new_state = 0;
129
130     if (!data->managed) return;
131     if (data->whole_window == root_window) return;
132
133     style = GetWindowLongW( data->hwnd, GWL_STYLE );
134     if (data->whole_rect.left <= 0 && data->whole_rect.right >= screen_width &&
135         data->whole_rect.top <= 0 && data->whole_rect.bottom >= screen_height)
136     {
137         if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
138             new_state |= (1 << NET_WM_STATE_MAXIMIZED);
139         else
140             new_state |= (1 << NET_WM_STATE_FULLSCREEN);
141     }
142     else if (style & WS_MAXIMIZE)
143         new_state |= (1 << NET_WM_STATE_MAXIMIZED);
144
145     ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
146     if (ex_style & WS_EX_TOPMOST)
147         new_state |= (1 << NET_WM_STATE_ABOVE);
148     if (ex_style & WS_EX_TOOLWINDOW)
149         new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR) | (1 << NET_WM_STATE_SKIP_PAGER);
150     if (!(ex_style & WS_EX_APPWINDOW) && GetWindow( data->hwnd, GW_OWNER ))
151         new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR);
152
153     if (!data->mapped)  /* set the _NET_WM_STATE atom directly */
154     {
155         Atom atoms[NB_NET_WM_STATES+1];
156         DWORD count;
157
158         for (i = count = 0; i < NB_NET_WM_STATES; i++)
159         {
160             if (!(new_state & (1 << i))) continue;
161             TRACE( "setting wm state %u for unmapped window %p/%lx\n",
162                    i, data->hwnd, data->whole_window );
163             atoms[count++] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
164             if (state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT)
165                 atoms[count++] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ);
166         }
167         wine_tsx11_lock();
168         XChangeProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), XA_ATOM,
169                          32, PropModeReplace, (unsigned char *)atoms, count );
170         wine_tsx11_unlock();
171     }
172     else  /* ask the window manager to do it for us */
173     {
174         XEvent xev;
175
176         xev.xclient.type = ClientMessage;
177         xev.xclient.window = data->whole_window;
178         xev.xclient.message_type = x11drv_atom(_NET_WM_STATE);
179         xev.xclient.serial = 0;
180         xev.xclient.display = display;
181         xev.xclient.send_event = True;
182         xev.xclient.format = 32;
183         xev.xclient.data.l[3] = 1;
184
185         for (i = 0; i < NB_NET_WM_STATES; i++)
186         {
187             if (!((data->net_wm_state ^ new_state) & (1 << i))) continue;  /* unchanged */
188
189             TRACE( "setting wm state %u for window %p/%lx to %u prev %u\n",
190                    i, data->hwnd, data->whole_window,
191                    (new_state & (1 << i)) != 0, (data->net_wm_state & (1 << i)) != 0 );
192
193             xev.xclient.data.l[0] = (new_state & (1 << i)) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
194             xev.xclient.data.l[1] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
195             xev.xclient.data.l[2] = ((state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT) ?
196                                      x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ) : 0);
197             wine_tsx11_lock();
198             XSendEvent( display, root_window, False,
199                         SubstructureRedirectMask | SubstructureNotifyMask, &xev );
200             wine_tsx11_unlock();
201         }
202     }
203     data->net_wm_state = new_state;
204 }
205
206
207 /***********************************************************************
208  *     set_xembed_flags
209  */
210 static void set_xembed_flags( Display *display, struct x11drv_win_data *data, unsigned long flags )
211 {
212     unsigned long info[2];
213
214     info[0] = 0; /* protocol version */
215     info[1] = flags;
216     wine_tsx11_lock();
217     XChangeProperty( display, data->whole_window, x11drv_atom(_XEMBED_INFO),
218                      x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 );
219     wine_tsx11_unlock();
220 }
221
222
223 /***********************************************************************
224  *     map_window
225  */
226 static void map_window( Display *display, struct x11drv_win_data *data, DWORD new_style )
227 {
228     TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
229
230     wait_for_withdrawn_state( display, data, TRUE );
231
232     if (!data->embedded)
233     {
234         update_net_wm_states( display, data );
235         X11DRV_sync_window_style( display, data );
236         wine_tsx11_lock();
237         XMapWindow( display, data->whole_window );
238         wine_tsx11_unlock();
239     }
240     else set_xembed_flags( display, data, XEMBED_MAPPED );
241
242     data->mapped = TRUE;
243     data->iconic = (new_style & WS_MINIMIZE) != 0;
244 }
245
246
247 /***********************************************************************
248  *     unmap_window
249  */
250 static void unmap_window( Display *display, struct x11drv_win_data *data )
251 {
252     TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
253
254     if (!data->embedded)
255     {
256         wait_for_withdrawn_state( display, data, FALSE );
257         wine_tsx11_lock();
258         if (data->managed) XWithdrawWindow( display, data->whole_window, DefaultScreen(display) );
259         else XUnmapWindow( display, data->whole_window );
260         wine_tsx11_unlock();
261     }
262     else set_xembed_flags( display, data, 0 );
263
264     data->mapped = FALSE;
265     data->net_wm_state = 0;
266 }
267
268
269 /***********************************************************************
270  *     make_window_embedded
271  */
272 void make_window_embedded( Display *display, struct x11drv_win_data *data )
273 {
274     if (data->mapped)
275     {
276         /* the window cannot be mapped before being embedded */
277         unmap_window( display, data );
278         data->embedded = TRUE;
279         map_window( display, data, 0 );
280     }
281     else
282     {
283         data->embedded = TRUE;
284         set_xembed_flags( display, data, 0 );
285     }
286 }
287
288
289 /***********************************************************************
290  *              SetWindowStyle   (X11DRV.@)
291  *
292  * Update the X state of a window to reflect a style change
293  */
294 void X11DRV_SetWindowStyle( HWND hwnd, DWORD old_style )
295 {
296     Display *display = thread_display();
297     struct x11drv_win_data *data;
298     DWORD new_style, changed;
299
300     if (hwnd == GetDesktopWindow()) return;
301     new_style = GetWindowLongW( hwnd, GWL_STYLE );
302     changed = new_style ^ old_style;
303
304     if ((changed & WS_VISIBLE) && (new_style & WS_VISIBLE))
305     {
306         /* we don't unmap windows, that causes trouble with the window manager */
307         if (!(data = X11DRV_get_win_data( hwnd )) &&
308             !(data = X11DRV_create_win_data( hwnd ))) return;
309
310         if (data->whole_window && X11DRV_is_window_rect_mapped( &data->window_rect ))
311         {
312             X11DRV_set_wm_hints( display, data );
313             if (!data->mapped) map_window( display, data, new_style );
314         }
315     }
316
317     if (changed & WS_DISABLED)
318     {
319         data = X11DRV_get_win_data( hwnd );
320         if (data && data->wm_hints)
321         {
322             wine_tsx11_lock();
323             data->wm_hints->input = !(new_style & WS_DISABLED);
324             XSetWMHints( display, data->whole_window, data->wm_hints );
325             wine_tsx11_unlock();
326         }
327     }
328 }
329
330
331 /***********************************************************************
332  *              move_window_bits
333  *
334  * Move the window bits when a window is moved.
335  */
336 static void move_window_bits( struct x11drv_win_data *data, const RECT *old_rect, const RECT *new_rect,
337                               const RECT *old_client_rect )
338 {
339     RECT src_rect = *old_rect;
340     RECT dst_rect = *new_rect;
341     HDC hdc_src, hdc_dst;
342     INT code;
343     HRGN rgn = 0;
344     HWND parent = 0;
345
346     if (!data->whole_window)
347     {
348         OffsetRect( &dst_rect, -data->window_rect.left, -data->window_rect.top );
349         parent = GetAncestor( data->hwnd, GA_PARENT );
350         hdc_src = GetDCEx( parent, 0, DCX_CACHE );
351         hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE | DCX_WINDOW );
352     }
353     else
354     {
355         OffsetRect( &dst_rect, -data->client_rect.left, -data->client_rect.top );
356         /* make src rect relative to the old position of the window */
357         OffsetRect( &src_rect, -old_client_rect->left, -old_client_rect->top );
358         if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
359         hdc_src = hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE );
360     }
361
362     code = X11DRV_START_EXPOSURES;
363     ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL );
364
365     TRACE( "copying bits for win %p/%lx/%lx %s -> %s\n",
366            data->hwnd, data->whole_window, data->client_window,
367            wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect) );
368     BitBlt( hdc_dst, dst_rect.left, dst_rect.top,
369             dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
370             hdc_src, src_rect.left, src_rect.top, SRCCOPY );
371
372     code = X11DRV_END_EXPOSURES;
373     ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(rgn), (LPSTR)&rgn );
374
375     ReleaseDC( data->hwnd, hdc_dst );
376     if (hdc_src != hdc_dst) ReleaseDC( parent, hdc_src );
377
378     if (rgn)
379     {
380         if (!data->whole_window)
381         {
382             /* map region to client rect since we are using DCX_WINDOW */
383             OffsetRgn( rgn, data->window_rect.left - data->client_rect.left,
384                        data->window_rect.top - data->client_rect.top );
385             RedrawWindow( data->hwnd, NULL, rgn,
386                           RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN );
387         }
388         else RedrawWindow( data->hwnd, NULL, rgn, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
389         DeleteObject( rgn );
390     }
391 }
392
393 /***********************************************************************
394  *              SetWindowPos   (X11DRV.@)
395  */
396 void X11DRV_SetWindowPos( HWND hwnd, HWND insert_after, UINT swp_flags,
397                           const RECT *rectWindow, const RECT *rectClient,
398                           const RECT *visible_rect, const RECT *valid_rects )
399 {
400     struct x11drv_thread_data *thread_data = x11drv_thread_data();
401     Display *display = thread_data->display;
402     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
403     DWORD new_style = GetWindowLongW( hwnd, GWL_STYLE );
404     RECT old_window_rect, old_whole_rect, old_client_rect;
405     int event_type;
406
407     if (!data)
408     {
409         /* create the win data if the window is being made visible */
410         if (!(new_style & WS_VISIBLE)) return;
411         if (!(data = X11DRV_create_win_data( hwnd ))) return;
412     }
413
414     /* check if we need to switch the window to managed */
415     if (!data->managed && data->whole_window && is_window_managed( hwnd, swp_flags, rectWindow ))
416     {
417         TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
418         if (data->mapped) unmap_window( display, data );
419         data->managed = TRUE;
420         SetPropA( hwnd, managed_prop, (HANDLE)1 );
421     }
422
423     old_window_rect = data->window_rect;
424     old_whole_rect  = data->whole_rect;
425     old_client_rect = data->client_rect;
426     data->window_rect = *rectWindow;
427     data->whole_rect  = *rectWindow;
428     data->client_rect = *rectClient;
429     X11DRV_window_to_X_rect( data, &data->whole_rect );
430     if (memcmp( visible_rect, &data->whole_rect, sizeof(RECT) ))
431     {
432         TRACE( "%p: need to update visible rect %s -> %s\n", hwnd,
433                wine_dbgstr_rect(visible_rect), wine_dbgstr_rect(&data->whole_rect) );
434         SERVER_START_REQ( set_window_visible_rect )
435         {
436             req->handle         = hwnd;
437             req->flags          = swp_flags;
438             req->visible.left   = data->whole_rect.left;
439             req->visible.top    = data->whole_rect.top;
440             req->visible.right  = data->whole_rect.right;
441             req->visible.bottom = data->whole_rect.bottom;
442             wine_server_call( req );
443         }
444         SERVER_END_REQ;
445     }
446
447     TRACE( "win %p window %s client %s style %08x flags %08x\n",
448            hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style, swp_flags );
449
450     if (!IsRectEmpty( &valid_rects[0] ))
451     {
452         int x_offset = old_whole_rect.left - data->whole_rect.left;
453         int y_offset = old_whole_rect.top - data->whole_rect.top;
454
455         /* if all that happened is that the whole window moved, copy everything */
456         if (!(swp_flags & SWP_FRAMECHANGED) &&
457             old_whole_rect.right   - data->whole_rect.right   == x_offset &&
458             old_whole_rect.bottom  - data->whole_rect.bottom  == y_offset &&
459             old_client_rect.left   - data->client_rect.left   == x_offset &&
460             old_client_rect.right  - data->client_rect.right  == x_offset &&
461             old_client_rect.top    - data->client_rect.top    == y_offset &&
462             old_client_rect.bottom - data->client_rect.bottom == y_offset &&
463             !memcmp( &valid_rects[0], &data->client_rect, sizeof(RECT) ))
464         {
465             /* if we have an X window the bits will be moved by the X server */
466             if (!data->whole_window)
467                 move_window_bits( data, &old_whole_rect, &data->whole_rect, &old_client_rect );
468         }
469         else
470             move_window_bits( data, &valid_rects[1], &valid_rects[0], &old_client_rect );
471     }
472
473     wine_tsx11_lock();
474     XFlush( gdi_display );  /* make sure painting is done before we move the window */
475     wine_tsx11_unlock();
476
477     X11DRV_sync_client_position( display, data, swp_flags, &old_client_rect, &old_whole_rect );
478
479     if (!data->whole_window) return;
480
481     /* check if we are currently processing an event relevant to this window */
482     event_type = 0;
483     if (thread_data->current_event && thread_data->current_event->xany.window == data->whole_window)
484         event_type = thread_data->current_event->type;
485
486     if (event_type != ConfigureNotify && event_type != PropertyNotify)
487         event_type = 0;  /* ignore other events */
488
489     if (data->mapped && (!(new_style & WS_VISIBLE) ||
490                          (!event_type && !X11DRV_is_window_rect_mapped( rectWindow ))))
491         unmap_window( display, data );
492
493     /* don't change position if we are about to minimize or maximize a managed window */
494     if (!event_type &&
495         !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE))))
496         X11DRV_sync_window_position( display, data, swp_flags, &old_client_rect, &old_whole_rect );
497
498     if ((new_style & WS_VISIBLE) &&
499         ((new_style & WS_MINIMIZE) || X11DRV_is_window_rect_mapped( rectWindow )))
500     {
501         if (!data->mapped || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)))
502             X11DRV_set_wm_hints( display, data );
503
504         if (!data->mapped)
505         {
506             map_window( display, data, new_style );
507         }
508         else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE)))
509         {
510             data->iconic = (new_style & WS_MINIMIZE) != 0;
511             TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic );
512             wine_tsx11_lock();
513             if (data->iconic)
514                 XIconifyWindow( display, data->whole_window, DefaultScreen(display) );
515             else if (X11DRV_is_window_rect_mapped( rectWindow ))
516                 XMapWindow( display, data->whole_window );
517             wine_tsx11_unlock();
518             update_net_wm_states( display, data );
519         }
520         else if (!event_type)
521         {
522             update_net_wm_states( display, data );
523         }
524     }
525
526     wine_tsx11_lock();
527     XFlush( display );  /* make sure changes are done before we start painting again */
528     wine_tsx11_unlock();
529 }
530
531
532 /**********************************************************************
533  *              X11DRV_MapNotify
534  */
535 void X11DRV_MapNotify( HWND hwnd, XEvent *event )
536 {
537     struct x11drv_win_data *data;
538
539     if (!(data = X11DRV_get_win_data( hwnd ))) return;
540     if (!data->mapped) return;
541
542     if (!data->managed)
543     {
544         HWND hwndFocus = GetFocus();
545         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
546     }
547 }
548
549
550 struct desktop_resize_data
551 {
552     RECT  old_screen_rect;
553     RECT  old_virtual_rect;
554 };
555
556 static BOOL CALLBACK update_windows_on_desktop_resize( HWND hwnd, LPARAM lparam )
557 {
558     struct x11drv_win_data *data;
559     Display *display = thread_display();
560     struct desktop_resize_data *resize_data = (struct desktop_resize_data *)lparam;
561     int mask = 0;
562
563     if (!(data = X11DRV_get_win_data( hwnd ))) return TRUE;
564
565     if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
566     {
567         /* update the full screen state */
568         update_net_wm_states( display, data );
569     }
570
571     if (resize_data->old_virtual_rect.left != virtual_screen_rect.left) mask |= CWX;
572     if (resize_data->old_virtual_rect.top != virtual_screen_rect.top) mask |= CWY;
573     if (mask && data->whole_window)
574     {
575         XWindowChanges changes;
576
577         wine_tsx11_lock();
578         changes.x = data->whole_rect.left - virtual_screen_rect.left;
579         changes.y = data->whole_rect.top - virtual_screen_rect.top;
580         XReconfigureWMWindow( display, data->whole_window,
581                               DefaultScreen(display), mask, &changes );
582         wine_tsx11_unlock();
583     }
584     return TRUE;
585 }
586
587
588 /***********************************************************************
589  *              X11DRV_resize_desktop
590  */
591 void X11DRV_resize_desktop( unsigned int width, unsigned int height )
592 {
593     HWND hwnd = GetDesktopWindow();
594     struct desktop_resize_data resize_data;
595
596     SetRect( &resize_data.old_screen_rect, 0, 0, screen_width, screen_height );
597     resize_data.old_virtual_rect = virtual_screen_rect;
598
599     xinerama_init( width, height );
600
601     if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId())
602     {
603         SendMessageW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, MAKELPARAM( width, height ) );
604     }
605     else
606     {
607         TRACE( "desktop %p change to (%dx%d)\n", hwnd, width, height );
608         SetWindowPos( hwnd, 0, virtual_screen_rect.left, virtual_screen_rect.top,
609                       virtual_screen_rect.right - virtual_screen_rect.left,
610                       virtual_screen_rect.bottom - virtual_screen_rect.top,
611                       SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE );
612         SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp,
613                              MAKELPARAM( width, height ), SMTO_ABORTIFHUNG, 2000, NULL );
614     }
615
616     EnumWindows( update_windows_on_desktop_resize, (LPARAM)&resize_data );
617 }
618
619
620 /***********************************************************************
621  *              X11DRV_ConfigureNotify
622  */
623 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
624 {
625     XConfigureEvent *event = &xev->xconfigure;
626     struct x11drv_win_data *data;
627     RECT rect;
628     UINT flags;
629     int cx, cy, x = event->x, y = event->y;
630
631     if (!hwnd) return;
632     if (!(data = X11DRV_get_win_data( hwnd ))) return;
633     if (!data->mapped || data->iconic) return;
634
635     /* Get geometry */
636
637     if (!event->send_event)  /* normal event, need to map coordinates to the root */
638     {
639         Window child;
640         wine_tsx11_lock();
641         XTranslateCoordinates( event->display, data->whole_window, root_window,
642                                0, 0, &x, &y, &child );
643         wine_tsx11_unlock();
644     }
645     rect.left   = x;
646     rect.top    = y;
647     rect.right  = x + event->width;
648     rect.bottom = y + event->height;
649     OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
650     TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
651            hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
652            event->x, event->y, event->width, event->height );
653     X11DRV_X_to_window_rect( data, &rect );
654
655     x     = rect.left;
656     y     = rect.top;
657     cx    = rect.right - rect.left;
658     cy    = rect.bottom - rect.top;
659     flags = SWP_NOACTIVATE | SWP_NOZORDER;
660
661     /* Compare what has changed */
662
663     GetWindowRect( hwnd, &rect );
664     if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
665     else
666         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
667                hwnd, rect.left, rect.top, x, y );
668
669     if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
670         (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
671     {
672         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
673         flags |= SWP_NOSIZE;
674     }
675     else
676         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
677                hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
678
679     SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
680 }
681
682
683 /***********************************************************************
684  *              is_netwm_supported
685  */
686 static BOOL is_netwm_supported( Display *display, Atom atom )
687 {
688     static Atom *net_supported;
689     static int net_supported_count = -1;
690     int i;
691
692     wine_tsx11_lock();
693     if (net_supported_count == -1)
694     {
695         Atom type;
696         int format;
697         unsigned long count, remaining;
698
699         if (!XGetWindowProperty( display, DefaultRootWindow(display), x11drv_atom(_NET_SUPPORTED), 0,
700                                  ~0UL, False, XA_ATOM, &type, &format, &count,
701                                  &remaining, (unsigned char **)&net_supported ))
702             net_supported_count = get_property_size( format, count ) / sizeof(Atom);
703         else
704             net_supported_count = 0;
705     }
706     wine_tsx11_unlock();
707
708     for (i = 0; i < net_supported_count; i++)
709         if (net_supported[i] == atom) return TRUE;
710     return FALSE;
711 }
712
713
714 /***********************************************************************
715  *           SysCommandSizeMove   (X11DRV.@)
716  *
717  * Perform SC_MOVE and SC_SIZE commands.
718  */
719 BOOL X11DRV_SysCommandSizeMove( HWND hwnd, WPARAM wparam )
720 {
721     WPARAM syscommand = wparam & 0xfff0;
722     WPARAM hittest = wparam & 0x0f;
723     DWORD dwPoint;
724     int x, y, dir;
725     XEvent xev;
726     Display *display = thread_display();
727     struct x11drv_win_data *data;
728
729     if (!(data = X11DRV_get_win_data( hwnd ))) return FALSE;
730     if (!data->whole_window || !data->managed) return FALSE;
731
732     if (!is_netwm_supported( display, x11drv_atom(_NET_WM_MOVERESIZE) ))
733     {
734         TRACE( "_NET_WM_MOVERESIZE not supported\n" );
735         return FALSE;
736     }
737
738     if (syscommand == SC_MOVE)
739     {
740         if (!hittest) dir = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
741         else dir = _NET_WM_MOVERESIZE_MOVE;
742     }
743     else
744     {
745         /* windows without WS_THICKFRAME are not resizable through the window manager */
746         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_THICKFRAME)) return FALSE;
747
748         switch (hittest)
749         {
750         case WMSZ_LEFT:        dir = _NET_WM_MOVERESIZE_SIZE_LEFT; break;
751         case WMSZ_RIGHT:       dir = _NET_WM_MOVERESIZE_SIZE_RIGHT; break;
752         case WMSZ_TOP:         dir = _NET_WM_MOVERESIZE_SIZE_TOP; break;
753         case WMSZ_TOPLEFT:     dir = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; break;
754         case WMSZ_TOPRIGHT:    dir = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; break;
755         case WMSZ_BOTTOM:      dir = _NET_WM_MOVERESIZE_SIZE_BOTTOM; break;
756         case WMSZ_BOTTOMLEFT:  dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; break;
757         case WMSZ_BOTTOMRIGHT: dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; break;
758         default:               dir = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; break;
759         }
760     }
761
762     dwPoint = GetMessagePos();
763     x = (short)LOWORD(dwPoint);
764     y = (short)HIWORD(dwPoint);
765
766     TRACE("hwnd %p, x %d, y %d, dir %d\n", hwnd, x, y, dir);
767
768     xev.xclient.type = ClientMessage;
769     xev.xclient.window = X11DRV_get_whole_window(hwnd);
770     xev.xclient.message_type = x11drv_atom(_NET_WM_MOVERESIZE);
771     xev.xclient.serial = 0;
772     xev.xclient.display = display;
773     xev.xclient.send_event = True;
774     xev.xclient.format = 32;
775     xev.xclient.data.l[0] = x; /* x coord */
776     xev.xclient.data.l[1] = y; /* y coord */
777     xev.xclient.data.l[2] = dir; /* direction */
778     xev.xclient.data.l[3] = 1; /* button */
779     xev.xclient.data.l[4] = 0; /* unused */
780
781     /* need to ungrab the pointer that may have been automatically grabbed
782      * with a ButtonPress event */
783     wine_tsx11_lock();
784     XUngrabPointer( display, CurrentTime );
785     XSendEvent(display, root_window, False, SubstructureNotifyMask, &xev);
786     wine_tsx11_unlock();
787     return TRUE;
788 }