winex11: Set the _NET_WM_STATE atom directly for unmapped windows.
[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  *              SetWindowStyle   (X11DRV.@)
115  *
116  * Update the X state of a window to reflect a style change
117  */
118 void X11DRV_SetWindowStyle( HWND hwnd, DWORD old_style )
119 {
120     Display *display = thread_display();
121     struct x11drv_win_data *data;
122     DWORD new_style, changed;
123
124     if (hwnd == GetDesktopWindow()) return;
125     new_style = GetWindowLongW( hwnd, GWL_STYLE );
126     changed = new_style ^ old_style;
127
128     if ((changed & WS_VISIBLE) && (new_style & WS_VISIBLE))
129     {
130         /* we don't unmap windows, that causes trouble with the window manager */
131         if (!(data = X11DRV_get_win_data( hwnd )) &&
132             !(data = X11DRV_create_win_data( hwnd ))) return;
133
134         if (data->whole_window && X11DRV_is_window_rect_mapped( &data->window_rect ))
135         {
136             X11DRV_set_wm_hints( display, data );
137             if (!data->mapped)
138             {
139                 TRACE( "mapping win %p/%lx\n", hwnd, data->whole_window );
140                 wait_for_withdrawn_state( display, data, TRUE );
141                 X11DRV_sync_window_style( display, data );
142                 wine_tsx11_lock();
143                 XMapWindow( display, data->whole_window );
144                 wine_tsx11_unlock();
145                 data->mapped = TRUE;
146                 data->iconic = (new_style & WS_MINIMIZE) != 0;
147             }
148         }
149     }
150
151     if (changed & WS_DISABLED)
152     {
153         data = X11DRV_get_win_data( hwnd );
154         if (data && data->wm_hints)
155         {
156             wine_tsx11_lock();
157             data->wm_hints->input = !(new_style & WS_DISABLED);
158             XSetWMHints( display, data->whole_window, data->wm_hints );
159             wine_tsx11_unlock();
160         }
161     }
162 }
163
164
165 /***********************************************************************
166  *     update_net_wm_states
167  */
168 static void update_net_wm_states( Display *display, struct x11drv_win_data *data )
169 {
170     static const unsigned int state_atoms[NB_NET_WM_STATES] =
171     {
172         XATOM__NET_WM_STATE_FULLSCREEN,
173         XATOM__NET_WM_STATE_ABOVE,
174         XATOM__NET_WM_STATE_MAXIMIZED_VERT,
175         XATOM__NET_WM_STATE_SKIP_PAGER,
176         XATOM__NET_WM_STATE_SKIP_TASKBAR
177     };
178
179     DWORD i, style, ex_style, new_state = 0;
180
181     if (!data->managed) return;
182
183     style = GetWindowLongW( data->hwnd, GWL_STYLE );
184     if (data->whole_rect.left <= 0 && data->whole_rect.right >= screen_width &&
185         data->whole_rect.top <= 0 && data->whole_rect.bottom >= screen_height)
186     {
187         if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
188             new_state |= (1 << NET_WM_STATE_MAXIMIZED);
189         else
190             new_state |= (1 << NET_WM_STATE_FULLSCREEN);
191     }
192     else if (style & WS_MAXIMIZE)
193         new_state |= (1 << NET_WM_STATE_MAXIMIZED);
194
195     ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
196     if (ex_style & WS_EX_TOPMOST)
197         new_state |= (1 << NET_WM_STATE_ABOVE);
198     if (ex_style & WS_EX_TOOLWINDOW)
199         new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR) | (1 << NET_WM_STATE_SKIP_PAGER);
200     if (!(ex_style & WS_EX_APPWINDOW) && GetWindow( data->hwnd, GW_OWNER ))
201         new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR);
202
203     if (!data->mapped)  /* set the _NET_WM_STATE atom directly */
204     {
205         Atom atoms[NB_NET_WM_STATES+1];
206         DWORD count;
207
208         for (i = count = 0; i < NB_NET_WM_STATES; i++)
209         {
210             if (!(new_state & (1 << i))) continue;
211             TRACE( "setting wm state %u for unmapped window %p/%lx\n",
212                    i, data->hwnd, data->whole_window );
213             atoms[count++] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
214             if (state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT)
215                 atoms[count++] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ);
216         }
217         wine_tsx11_lock();
218         XChangeProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), XA_ATOM,
219                          32, PropModeReplace, (unsigned char *)atoms, count );
220         wine_tsx11_unlock();
221     }
222     else  /* ask the window manager to do it for us */
223     {
224         XEvent xev;
225
226         xev.xclient.type = ClientMessage;
227         xev.xclient.window = data->whole_window;
228         xev.xclient.message_type = x11drv_atom(_NET_WM_STATE);
229         xev.xclient.serial = 0;
230         xev.xclient.display = display;
231         xev.xclient.send_event = True;
232         xev.xclient.format = 32;
233         xev.xclient.data.l[3] = 1;
234
235         for (i = 0; i < NB_NET_WM_STATES; i++)
236         {
237             if (!((data->net_wm_state ^ new_state) & (1 << i))) continue;  /* unchanged */
238
239             TRACE( "setting wm state %u for window %p/%lx to %u prev %u\n",
240                    i, data->hwnd, data->whole_window,
241                    (new_state & (1 << i)) != 0, (data->net_wm_state & (1 << i)) != 0 );
242
243             xev.xclient.data.l[0] = (new_state & (1 << i)) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
244             xev.xclient.data.l[1] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
245             xev.xclient.data.l[2] = ((state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT) ?
246                                      x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ) : 0);
247             wine_tsx11_lock();
248             XSendEvent( display, root_window, False,
249                         SubstructureRedirectMask | SubstructureNotifyMask, &xev );
250             wine_tsx11_unlock();
251         }
252     }
253     data->net_wm_state = new_state;
254 }
255
256
257 /***********************************************************************
258  *              move_window_bits
259  *
260  * Move the window bits when a window is moved.
261  */
262 static void move_window_bits( struct x11drv_win_data *data, const RECT *old_rect, const RECT *new_rect,
263                               const RECT *old_client_rect )
264 {
265     RECT src_rect = *old_rect;
266     RECT dst_rect = *new_rect;
267     HDC hdc_src, hdc_dst;
268     INT code;
269     HRGN rgn = 0;
270     HWND parent = 0;
271
272     if (!data->whole_window)
273     {
274         OffsetRect( &dst_rect, -data->window_rect.left, -data->window_rect.top );
275         parent = GetAncestor( data->hwnd, GA_PARENT );
276         hdc_src = GetDCEx( parent, 0, DCX_CACHE );
277         hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE | DCX_WINDOW );
278     }
279     else
280     {
281         OffsetRect( &dst_rect, -data->client_rect.left, -data->client_rect.top );
282         /* make src rect relative to the old position of the window */
283         OffsetRect( &src_rect, -old_client_rect->left, -old_client_rect->top );
284         if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
285         hdc_src = hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE );
286     }
287
288     code = X11DRV_START_EXPOSURES;
289     ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL );
290
291     TRACE( "copying bits for win %p/%lx/%lx %s -> %s\n",
292            data->hwnd, data->whole_window, data->client_window,
293            wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect) );
294     BitBlt( hdc_dst, dst_rect.left, dst_rect.top,
295             dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
296             hdc_src, src_rect.left, src_rect.top, SRCCOPY );
297
298     code = X11DRV_END_EXPOSURES;
299     ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(rgn), (LPSTR)&rgn );
300
301     ReleaseDC( data->hwnd, hdc_dst );
302     if (hdc_src != hdc_dst) ReleaseDC( parent, hdc_src );
303
304     if (rgn)
305     {
306         if (!data->whole_window)
307         {
308             /* map region to client rect since we are using DCX_WINDOW */
309             OffsetRgn( rgn, data->window_rect.left - data->client_rect.left,
310                        data->window_rect.top - data->client_rect.top );
311             RedrawWindow( data->hwnd, NULL, rgn,
312                           RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN );
313         }
314         else RedrawWindow( data->hwnd, NULL, rgn, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
315         DeleteObject( rgn );
316     }
317 }
318
319 /***********************************************************************
320  *              SetWindowPos   (X11DRV.@)
321  */
322 void X11DRV_SetWindowPos( HWND hwnd, HWND insert_after, UINT swp_flags,
323                           const RECT *rectWindow, const RECT *rectClient,
324                           const RECT *visible_rect, const RECT *valid_rects )
325 {
326     struct x11drv_thread_data *thread_data = x11drv_thread_data();
327     Display *display = thread_data->display;
328     struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
329     DWORD new_style = GetWindowLongW( hwnd, GWL_STYLE );
330     RECT old_window_rect, old_whole_rect, old_client_rect;
331     int event_type;
332
333     if (!data)
334     {
335         /* create the win data if the window is being made visible */
336         if (!(new_style & WS_VISIBLE)) return;
337         if (!(data = X11DRV_create_win_data( hwnd ))) return;
338     }
339
340     /* check if we need to switch the window to managed */
341     if (!data->managed && data->whole_window && is_window_managed( hwnd, swp_flags, rectWindow ))
342     {
343         TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
344         data->managed = TRUE;
345         SetPropA( hwnd, managed_prop, (HANDLE)1 );
346         if (data->mapped)
347         {
348             wine_tsx11_lock();
349             XUnmapWindow( display, data->whole_window );
350             wine_tsx11_unlock();
351             data->mapped = FALSE;
352         }
353     }
354
355     old_window_rect = data->window_rect;
356     old_whole_rect  = data->whole_rect;
357     old_client_rect = data->client_rect;
358     data->window_rect = *rectWindow;
359     data->whole_rect  = *rectWindow;
360     data->client_rect = *rectClient;
361     X11DRV_window_to_X_rect( data, &data->whole_rect );
362     if (memcmp( visible_rect, &data->whole_rect, sizeof(RECT) ))
363     {
364         TRACE( "%p: need to update visible rect %s -> %s\n", hwnd,
365                wine_dbgstr_rect(visible_rect), wine_dbgstr_rect(&data->whole_rect) );
366         SERVER_START_REQ( set_window_visible_rect )
367         {
368             req->handle         = hwnd;
369             req->flags          = swp_flags;
370             req->visible.left   = data->whole_rect.left;
371             req->visible.top    = data->whole_rect.top;
372             req->visible.right  = data->whole_rect.right;
373             req->visible.bottom = data->whole_rect.bottom;
374             wine_server_call( req );
375         }
376         SERVER_END_REQ;
377     }
378
379     TRACE( "win %p window %s client %s style %08x flags %08x\n",
380            hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style, swp_flags );
381
382     if (!IsRectEmpty( &valid_rects[0] ))
383     {
384         int x_offset = old_whole_rect.left - data->whole_rect.left;
385         int y_offset = old_whole_rect.top - data->whole_rect.top;
386
387         /* if all that happened is that the whole window moved, copy everything */
388         if (!(swp_flags & SWP_FRAMECHANGED) &&
389             old_whole_rect.right   - data->whole_rect.right   == x_offset &&
390             old_whole_rect.bottom  - data->whole_rect.bottom  == y_offset &&
391             old_client_rect.left   - data->client_rect.left   == x_offset &&
392             old_client_rect.right  - data->client_rect.right  == x_offset &&
393             old_client_rect.top    - data->client_rect.top    == y_offset &&
394             old_client_rect.bottom - data->client_rect.bottom == y_offset &&
395             !memcmp( &valid_rects[0], &data->client_rect, sizeof(RECT) ))
396         {
397             /* if we have an X window the bits will be moved by the X server */
398             if (!data->whole_window)
399                 move_window_bits( data, &old_whole_rect, &data->whole_rect, &old_client_rect );
400         }
401         else
402             move_window_bits( data, &valid_rects[1], &valid_rects[0], &old_client_rect );
403     }
404
405     X11DRV_sync_client_position( display, data, swp_flags, &old_client_rect, &old_whole_rect );
406
407     if (!data->whole_window) return;
408
409     /* check if we are currently processing an event relevant to this window */
410     event_type = 0;
411     if (thread_data->current_event && thread_data->current_event->xany.window == data->whole_window)
412         event_type = thread_data->current_event->type;
413
414     if (event_type != ConfigureNotify && event_type != PropertyNotify)
415         event_type = 0;  /* ignore other events */
416
417     if (data->mapped && (!(new_style & WS_VISIBLE) ||
418                          (!event_type && !X11DRV_is_window_rect_mapped( rectWindow ))))
419     {
420         TRACE( "unmapping win %p/%lx\n", hwnd, data->whole_window );
421         wait_for_withdrawn_state( display, data, FALSE );
422         wine_tsx11_lock();
423         if (data->managed) XWithdrawWindow( display, data->whole_window, DefaultScreen(display) );
424         else XUnmapWindow( display, data->whole_window );
425         wine_tsx11_unlock();
426         data->mapped = FALSE;
427         data->net_wm_state = 0;
428     }
429
430     /* don't change position if we are about to minimize or maximize a managed window */
431     if (!event_type &&
432         !(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE))))
433         X11DRV_sync_window_position( display, data, swp_flags, &old_client_rect, &old_whole_rect );
434
435     if ((new_style & WS_VISIBLE) &&
436         ((new_style & WS_MINIMIZE) || X11DRV_is_window_rect_mapped( rectWindow )))
437     {
438         if (!data->mapped || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)))
439             X11DRV_set_wm_hints( display, data );
440
441         if (!data->mapped)
442         {
443             TRACE( "mapping win %p/%lx\n", hwnd, data->whole_window );
444             wait_for_withdrawn_state( display, data, TRUE );
445             update_net_wm_states( display, data );
446             X11DRV_sync_window_style( display, data );
447             wine_tsx11_lock();
448             XMapWindow( display, data->whole_window );
449             XFlush( display );
450             wine_tsx11_unlock();
451             data->mapped = TRUE;
452             data->iconic = (new_style & WS_MINIMIZE) != 0;
453         }
454         else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE)))
455         {
456             data->iconic = (new_style & WS_MINIMIZE) != 0;
457             TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic );
458             wine_tsx11_lock();
459             if (data->iconic)
460                 XIconifyWindow( display, data->whole_window, DefaultScreen(display) );
461             else if (X11DRV_is_window_rect_mapped( rectWindow ))
462                 XMapWindow( display, data->whole_window );
463             wine_tsx11_unlock();
464             update_net_wm_states( display, data );
465         }
466         else if (!event_type)
467         {
468             update_net_wm_states( display, data );
469         }
470     }
471 }
472
473
474 /**********************************************************************
475  *              X11DRV_MapNotify
476  */
477 void X11DRV_MapNotify( HWND hwnd, XEvent *event )
478 {
479     struct x11drv_win_data *data;
480
481     if (!(data = X11DRV_get_win_data( hwnd ))) return;
482     if (!data->mapped) return;
483
484     if (!data->managed)
485     {
486         HWND hwndFocus = GetFocus();
487         if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus);  /* FIXME */
488     }
489 }
490
491
492 struct desktop_resize_data
493 {
494     RECT  old_screen_rect;
495     RECT  old_virtual_rect;
496 };
497
498 static BOOL CALLBACK update_windows_on_desktop_resize( HWND hwnd, LPARAM lparam )
499 {
500     struct x11drv_win_data *data;
501     Display *display = thread_display();
502     struct desktop_resize_data *resize_data = (struct desktop_resize_data *)lparam;
503     int mask = 0;
504
505     if (!(data = X11DRV_get_win_data( hwnd ))) return TRUE;
506
507     if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
508     {
509         /* update the full screen state */
510         update_net_wm_states( display, data );
511     }
512
513     if (resize_data->old_virtual_rect.left != virtual_screen_rect.left) mask |= CWX;
514     if (resize_data->old_virtual_rect.top != virtual_screen_rect.top) mask |= CWY;
515     if (mask && data->whole_window)
516     {
517         XWindowChanges changes;
518
519         wine_tsx11_lock();
520         changes.x = data->whole_rect.left - virtual_screen_rect.left;
521         changes.y = data->whole_rect.top - virtual_screen_rect.top;
522         XReconfigureWMWindow( display, data->whole_window,
523                               DefaultScreen(display), mask, &changes );
524         wine_tsx11_unlock();
525     }
526     return TRUE;
527 }
528
529
530 /***********************************************************************
531  *              X11DRV_resize_desktop
532  */
533 void X11DRV_resize_desktop( unsigned int width, unsigned int height )
534 {
535     HWND hwnd = GetDesktopWindow();
536     struct desktop_resize_data resize_data;
537
538     SetRect( &resize_data.old_screen_rect, 0, 0, screen_width, screen_height );
539     resize_data.old_virtual_rect = virtual_screen_rect;
540
541     xinerama_init( width, height );
542
543     if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId())
544     {
545         SendMessageW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, MAKELPARAM( width, height ) );
546     }
547     else
548     {
549         TRACE( "desktop %p change to (%dx%d)\n", hwnd, width, height );
550         SetWindowPos( hwnd, 0, virtual_screen_rect.left, virtual_screen_rect.top,
551                       virtual_screen_rect.right - virtual_screen_rect.left,
552                       virtual_screen_rect.bottom - virtual_screen_rect.top,
553                       SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE );
554         SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp,
555                              MAKELPARAM( width, height ), SMTO_ABORTIFHUNG, 2000, NULL );
556     }
557
558     EnumWindows( update_windows_on_desktop_resize, (LPARAM)&resize_data );
559 }
560
561
562 /***********************************************************************
563  *              X11DRV_ConfigureNotify
564  */
565 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
566 {
567     XConfigureEvent *event = &xev->xconfigure;
568     struct x11drv_win_data *data;
569     RECT rect;
570     UINT flags;
571     int cx, cy, x = event->x, y = event->y;
572
573     if (!hwnd) return;
574     if (!(data = X11DRV_get_win_data( hwnd ))) return;
575     if (!data->mapped || data->iconic) return;
576
577     /* Get geometry */
578
579     if (!event->send_event)  /* normal event, need to map coordinates to the root */
580     {
581         Window child;
582         wine_tsx11_lock();
583         XTranslateCoordinates( event->display, data->whole_window, root_window,
584                                0, 0, &x, &y, &child );
585         wine_tsx11_unlock();
586     }
587     rect.left   = x;
588     rect.top    = y;
589     rect.right  = x + event->width;
590     rect.bottom = y + event->height;
591     OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
592     TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
593            hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
594            event->x, event->y, event->width, event->height );
595     X11DRV_X_to_window_rect( data, &rect );
596
597     x     = rect.left;
598     y     = rect.top;
599     cx    = rect.right - rect.left;
600     cy    = rect.bottom - rect.top;
601     flags = SWP_NOACTIVATE | SWP_NOZORDER;
602
603     /* Compare what has changed */
604
605     GetWindowRect( hwnd, &rect );
606     if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
607     else
608         TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
609                hwnd, rect.left, rect.top, x, y );
610
611     if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
612         (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
613     {
614         if (flags & SWP_NOMOVE) return;  /* if nothing changed, don't do anything */
615         flags |= SWP_NOSIZE;
616     }
617     else
618         TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
619                hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
620
621     SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
622 }
623
624
625 /***********************************************************************
626  *              is_netwm_supported
627  */
628 static BOOL is_netwm_supported( Display *display, Atom atom )
629 {
630     static Atom *net_supported;
631     static int net_supported_count = -1;
632     int i;
633
634     wine_tsx11_lock();
635     if (net_supported_count == -1)
636     {
637         Atom type;
638         int format;
639         unsigned long count, remaining;
640
641         if (!XGetWindowProperty( display, DefaultRootWindow(display), x11drv_atom(_NET_SUPPORTED), 0,
642                                  ~0UL, False, XA_ATOM, &type, &format, &count,
643                                  &remaining, (unsigned char **)&net_supported ))
644             net_supported_count = get_property_size( format, count ) / sizeof(Atom);
645         else
646             net_supported_count = 0;
647     }
648     wine_tsx11_unlock();
649
650     for (i = 0; i < net_supported_count; i++)
651         if (net_supported[i] == atom) return TRUE;
652     return FALSE;
653 }
654
655
656 /***********************************************************************
657  *           SysCommandSizeMove   (X11DRV.@)
658  *
659  * Perform SC_MOVE and SC_SIZE commands.
660  */
661 BOOL X11DRV_SysCommandSizeMove( HWND hwnd, WPARAM wparam )
662 {
663     WPARAM syscommand = wparam & 0xfff0;
664     WPARAM hittest = wparam & 0x0f;
665     DWORD dwPoint;
666     int x, y, dir;
667     XEvent xev;
668     Display *display = thread_display();
669     struct x11drv_win_data *data;
670
671     if (!(data = X11DRV_get_win_data( hwnd ))) return FALSE;
672     if (!data->whole_window || !data->managed) return FALSE;
673
674     if (!is_netwm_supported( display, x11drv_atom(_NET_WM_MOVERESIZE) ))
675     {
676         TRACE( "_NET_WM_MOVERESIZE not supported\n" );
677         return FALSE;
678     }
679
680     if (syscommand == SC_MOVE)
681     {
682         if (!hittest) dir = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
683         else dir = _NET_WM_MOVERESIZE_MOVE;
684     }
685     else
686     {
687         /* windows without WS_THICKFRAME are not resizable through the window manager */
688         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_THICKFRAME)) return FALSE;
689
690         switch (hittest)
691         {
692         case WMSZ_LEFT:        dir = _NET_WM_MOVERESIZE_SIZE_LEFT; break;
693         case WMSZ_RIGHT:       dir = _NET_WM_MOVERESIZE_SIZE_RIGHT; break;
694         case WMSZ_TOP:         dir = _NET_WM_MOVERESIZE_SIZE_TOP; break;
695         case WMSZ_TOPLEFT:     dir = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; break;
696         case WMSZ_TOPRIGHT:    dir = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; break;
697         case WMSZ_BOTTOM:      dir = _NET_WM_MOVERESIZE_SIZE_BOTTOM; break;
698         case WMSZ_BOTTOMLEFT:  dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; break;
699         case WMSZ_BOTTOMRIGHT: dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; break;
700         default:               dir = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; break;
701         }
702     }
703
704     dwPoint = GetMessagePos();
705     x = (short)LOWORD(dwPoint);
706     y = (short)HIWORD(dwPoint);
707
708     TRACE("hwnd %p, x %d, y %d, dir %d\n", hwnd, x, y, dir);
709
710     xev.xclient.type = ClientMessage;
711     xev.xclient.window = X11DRV_get_whole_window(hwnd);
712     xev.xclient.message_type = x11drv_atom(_NET_WM_MOVERESIZE);
713     xev.xclient.serial = 0;
714     xev.xclient.display = display;
715     xev.xclient.send_event = True;
716     xev.xclient.format = 32;
717     xev.xclient.data.l[0] = x; /* x coord */
718     xev.xclient.data.l[1] = y; /* y coord */
719     xev.xclient.data.l[2] = dir; /* direction */
720     xev.xclient.data.l[3] = 1; /* button */
721     xev.xclient.data.l[4] = 0; /* unused */
722
723     /* need to ungrab the pointer that may have been automatically grabbed
724      * with a ButtonPress event */
725     wine_tsx11_lock();
726     XUngrabPointer( display, CurrentTime );
727     XSendEvent(display, root_window, False, SubstructureNotifyMask, &xev);
728     wine_tsx11_unlock();
729     return TRUE;
730 }