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