winex11: Moved the WM_SYSCOMMAND handling to window.c.
[wine/multimedia.git] / dlls / winex11.drv / winpos.c
blob2f7ab13968c02c6e01e6a3e031c60b0c4a18b9f9
1 /*
2 * Window position related functions.
4 * Copyright 1993, 1994, 1995, 2001 Alexandre Julliard
5 * Copyright 1995, 1996, 1999 Alex Korobka
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.
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.
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
22 #include "config.h"
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 #include <stdarg.h>
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"
35 #include "x11drv.h"
37 #include "wine/server.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
42 #define SWP_AGG_NOPOSCHANGE \
43 (SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
45 #define _NET_WM_STATE_REMOVE 0
46 #define _NET_WM_STATE_ADD 1
47 #define _NET_WM_STATE_TOGGLE 2
49 static const char managed_prop[] = "__wine_x11_managed";
51 /***********************************************************************
52 * update_net_wm_states
54 static void update_net_wm_states( Display *display, struct x11drv_win_data *data )
56 static const unsigned int state_atoms[NB_NET_WM_STATES] =
58 XATOM__NET_WM_STATE_FULLSCREEN,
59 XATOM__NET_WM_STATE_ABOVE,
60 XATOM__NET_WM_STATE_MAXIMIZED_VERT,
61 XATOM__NET_WM_STATE_SKIP_PAGER,
62 XATOM__NET_WM_STATE_SKIP_TASKBAR
65 DWORD i, style, ex_style, new_state = 0;
67 if (!data->managed) return;
68 if (data->whole_window == root_window) return;
70 style = GetWindowLongW( data->hwnd, GWL_STYLE );
71 if (data->whole_rect.left <= 0 && data->whole_rect.right >= screen_width &&
72 data->whole_rect.top <= 0 && data->whole_rect.bottom >= screen_height)
74 if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
75 new_state |= (1 << NET_WM_STATE_MAXIMIZED);
76 else if (!(style & WS_MINIMIZE))
77 new_state |= (1 << NET_WM_STATE_FULLSCREEN);
79 else if (style & WS_MAXIMIZE)
80 new_state |= (1 << NET_WM_STATE_MAXIMIZED);
82 ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
83 if (ex_style & WS_EX_TOPMOST)
84 new_state |= (1 << NET_WM_STATE_ABOVE);
85 if (ex_style & WS_EX_TOOLWINDOW)
86 new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR) | (1 << NET_WM_STATE_SKIP_PAGER);
87 if (!(ex_style & WS_EX_APPWINDOW) && GetWindow( data->hwnd, GW_OWNER ))
88 new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR);
90 if (!data->mapped) /* set the _NET_WM_STATE atom directly */
92 Atom atoms[NB_NET_WM_STATES+1];
93 DWORD count;
95 for (i = count = 0; i < NB_NET_WM_STATES; i++)
97 if (!(new_state & (1 << i))) continue;
98 TRACE( "setting wm state %u for unmapped window %p/%lx\n",
99 i, data->hwnd, data->whole_window );
100 atoms[count++] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
101 if (state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT)
102 atoms[count++] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ);
104 wine_tsx11_lock();
105 XChangeProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), XA_ATOM,
106 32, PropModeReplace, (unsigned char *)atoms, count );
107 wine_tsx11_unlock();
109 else /* ask the window manager to do it for us */
111 XEvent xev;
113 xev.xclient.type = ClientMessage;
114 xev.xclient.window = data->whole_window;
115 xev.xclient.message_type = x11drv_atom(_NET_WM_STATE);
116 xev.xclient.serial = 0;
117 xev.xclient.display = display;
118 xev.xclient.send_event = True;
119 xev.xclient.format = 32;
120 xev.xclient.data.l[3] = 1;
122 for (i = 0; i < NB_NET_WM_STATES; i++)
124 if (!((data->net_wm_state ^ new_state) & (1 << i))) continue; /* unchanged */
126 TRACE( "setting wm state %u for window %p/%lx to %u prev %u\n",
127 i, data->hwnd, data->whole_window,
128 (new_state & (1 << i)) != 0, (data->net_wm_state & (1 << i)) != 0 );
130 xev.xclient.data.l[0] = (new_state & (1 << i)) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
131 xev.xclient.data.l[1] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
132 xev.xclient.data.l[2] = ((state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT) ?
133 x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ) : 0);
134 wine_tsx11_lock();
135 XSendEvent( display, root_window, False,
136 SubstructureRedirectMask | SubstructureNotifyMask, &xev );
137 wine_tsx11_unlock();
140 data->net_wm_state = new_state;
144 /***********************************************************************
145 * set_xembed_flags
147 static void set_xembed_flags( Display *display, struct x11drv_win_data *data, unsigned long flags )
149 unsigned long info[2];
151 info[0] = 0; /* protocol version */
152 info[1] = flags;
153 wine_tsx11_lock();
154 XChangeProperty( display, data->whole_window, x11drv_atom(_XEMBED_INFO),
155 x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 );
156 wine_tsx11_unlock();
160 /***********************************************************************
161 * map_window
163 static void map_window( Display *display, struct x11drv_win_data *data, DWORD new_style )
165 TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
167 wait_for_withdrawn_state( display, data, TRUE );
169 if (!data->embedded)
171 update_net_wm_states( display, data );
172 X11DRV_sync_window_style( display, data );
173 wine_tsx11_lock();
174 XMapWindow( display, data->whole_window );
175 wine_tsx11_unlock();
177 else set_xembed_flags( display, data, XEMBED_MAPPED );
179 data->mapped = TRUE;
180 data->iconic = (new_style & WS_MINIMIZE) != 0;
184 /***********************************************************************
185 * unmap_window
187 static void unmap_window( Display *display, struct x11drv_win_data *data )
189 TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
191 if (!data->embedded)
193 wait_for_withdrawn_state( display, data, FALSE );
194 wine_tsx11_lock();
195 if (data->managed) XWithdrawWindow( display, data->whole_window, DefaultScreen(display) );
196 else XUnmapWindow( display, data->whole_window );
197 wine_tsx11_unlock();
199 else set_xembed_flags( display, data, 0 );
201 data->mapped = FALSE;
202 data->net_wm_state = 0;
206 /***********************************************************************
207 * make_window_embedded
209 void make_window_embedded( Display *display, struct x11drv_win_data *data )
211 if (data->mapped)
213 /* the window cannot be mapped before being embedded */
214 unmap_window( display, data );
215 data->embedded = TRUE;
216 map_window( display, data, 0 );
218 else
220 data->embedded = TRUE;
221 set_xembed_flags( display, data, 0 );
226 /***********************************************************************
227 * SetWindowStyle (X11DRV.@)
229 * Update the X state of a window to reflect a style change
231 void X11DRV_SetWindowStyle( HWND hwnd, DWORD old_style )
233 Display *display = thread_display();
234 struct x11drv_win_data *data;
235 DWORD new_style, changed;
237 if (hwnd == GetDesktopWindow()) return;
238 new_style = GetWindowLongW( hwnd, GWL_STYLE );
239 changed = new_style ^ old_style;
241 if ((changed & WS_VISIBLE) && (new_style & WS_VISIBLE))
243 /* we don't unmap windows, that causes trouble with the window manager */
244 if (!(data = X11DRV_get_win_data( hwnd )) &&
245 !(data = X11DRV_create_win_data( hwnd ))) return;
247 if (data->whole_window && X11DRV_is_window_rect_mapped( &data->window_rect ))
249 X11DRV_set_wm_hints( display, data );
250 if (!data->mapped) map_window( display, data, new_style );
254 if (changed & WS_DISABLED)
256 data = X11DRV_get_win_data( hwnd );
257 if (data && data->wm_hints)
259 wine_tsx11_lock();
260 data->wm_hints->input = !(new_style & WS_DISABLED);
261 XSetWMHints( display, data->whole_window, data->wm_hints );
262 wine_tsx11_unlock();
268 /***********************************************************************
269 * move_window_bits
271 * Move the window bits when a window is moved.
273 static void move_window_bits( struct x11drv_win_data *data, const RECT *old_rect, const RECT *new_rect,
274 const RECT *old_client_rect )
276 RECT src_rect = *old_rect;
277 RECT dst_rect = *new_rect;
278 HDC hdc_src, hdc_dst;
279 INT code;
280 HRGN rgn = 0;
281 HWND parent = 0;
283 if (!data->whole_window)
285 OffsetRect( &dst_rect, -data->window_rect.left, -data->window_rect.top );
286 parent = GetAncestor( data->hwnd, GA_PARENT );
287 hdc_src = GetDCEx( parent, 0, DCX_CACHE );
288 hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE | DCX_WINDOW );
290 else
292 OffsetRect( &dst_rect, -data->client_rect.left, -data->client_rect.top );
293 /* make src rect relative to the old position of the window */
294 OffsetRect( &src_rect, -old_client_rect->left, -old_client_rect->top );
295 if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
296 hdc_src = hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE );
299 code = X11DRV_START_EXPOSURES;
300 ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL );
302 TRACE( "copying bits for win %p/%lx/%lx %s -> %s\n",
303 data->hwnd, data->whole_window, data->client_window,
304 wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect) );
305 BitBlt( hdc_dst, dst_rect.left, dst_rect.top,
306 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
307 hdc_src, src_rect.left, src_rect.top, SRCCOPY );
309 code = X11DRV_END_EXPOSURES;
310 ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(rgn), (LPSTR)&rgn );
312 ReleaseDC( data->hwnd, hdc_dst );
313 if (hdc_src != hdc_dst) ReleaseDC( parent, hdc_src );
315 if (rgn)
317 if (!data->whole_window)
319 /* map region to client rect since we are using DCX_WINDOW */
320 OffsetRgn( rgn, data->window_rect.left - data->client_rect.left,
321 data->window_rect.top - data->client_rect.top );
322 RedrawWindow( data->hwnd, NULL, rgn,
323 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN );
325 else RedrawWindow( data->hwnd, NULL, rgn, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
326 DeleteObject( rgn );
330 /***********************************************************************
331 * SetWindowPos (X11DRV.@)
333 void X11DRV_SetWindowPos( HWND hwnd, HWND insert_after, UINT swp_flags,
334 const RECT *rectWindow, const RECT *rectClient,
335 const RECT *visible_rect, const RECT *valid_rects )
337 struct x11drv_thread_data *thread_data = x11drv_thread_data();
338 Display *display = thread_data->display;
339 struct x11drv_win_data *data = X11DRV_get_win_data( hwnd );
340 DWORD new_style = GetWindowLongW( hwnd, GWL_STYLE );
341 RECT old_window_rect, old_whole_rect, old_client_rect;
342 int event_type;
344 if (!data)
346 /* create the win data if the window is being made visible */
347 if (!(new_style & WS_VISIBLE)) return;
348 if (!(data = X11DRV_create_win_data( hwnd ))) return;
351 /* check if we need to switch the window to managed */
352 if (!data->managed && data->whole_window && is_window_managed( hwnd, swp_flags, rectWindow ))
354 TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
355 if (data->mapped) unmap_window( display, data );
356 data->managed = TRUE;
357 SetPropA( hwnd, managed_prop, (HANDLE)1 );
360 old_window_rect = data->window_rect;
361 old_whole_rect = data->whole_rect;
362 old_client_rect = data->client_rect;
363 data->window_rect = *rectWindow;
364 data->whole_rect = *rectWindow;
365 data->client_rect = *rectClient;
366 X11DRV_window_to_X_rect( data, &data->whole_rect );
367 if (memcmp( visible_rect, &data->whole_rect, sizeof(RECT) ))
369 TRACE( "%p: need to update visible rect %s -> %s\n", hwnd,
370 wine_dbgstr_rect(visible_rect), wine_dbgstr_rect(&data->whole_rect) );
371 SERVER_START_REQ( set_window_visible_rect )
373 req->handle = hwnd;
374 req->flags = swp_flags;
375 req->visible.left = data->whole_rect.left;
376 req->visible.top = data->whole_rect.top;
377 req->visible.right = data->whole_rect.right;
378 req->visible.bottom = data->whole_rect.bottom;
379 wine_server_call( req );
381 SERVER_END_REQ;
384 TRACE( "win %p window %s client %s style %08x flags %08x\n",
385 hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style, swp_flags );
387 if (!IsRectEmpty( &valid_rects[0] ))
389 int x_offset = old_whole_rect.left - data->whole_rect.left;
390 int y_offset = old_whole_rect.top - data->whole_rect.top;
392 /* if all that happened is that the whole window moved, copy everything */
393 if (!(swp_flags & SWP_FRAMECHANGED) &&
394 old_whole_rect.right - data->whole_rect.right == x_offset &&
395 old_whole_rect.bottom - data->whole_rect.bottom == y_offset &&
396 old_client_rect.left - data->client_rect.left == x_offset &&
397 old_client_rect.right - data->client_rect.right == x_offset &&
398 old_client_rect.top - data->client_rect.top == y_offset &&
399 old_client_rect.bottom - data->client_rect.bottom == y_offset &&
400 !memcmp( &valid_rects[0], &data->client_rect, sizeof(RECT) ))
402 /* if we have an X window the bits will be moved by the X server */
403 if (!data->whole_window)
404 move_window_bits( data, &old_whole_rect, &data->whole_rect, &old_client_rect );
406 else
407 move_window_bits( data, &valid_rects[1], &valid_rects[0], &old_client_rect );
410 wine_tsx11_lock();
411 XFlush( gdi_display ); /* make sure painting is done before we move the window */
412 wine_tsx11_unlock();
414 X11DRV_sync_client_position( display, data, swp_flags, &old_client_rect, &old_whole_rect );
416 if (!data->whole_window) return;
418 /* check if we are currently processing an event relevant to this window */
419 event_type = 0;
420 if (thread_data->current_event && thread_data->current_event->xany.window == data->whole_window)
421 event_type = thread_data->current_event->type;
423 if (event_type != ConfigureNotify && event_type != PropertyNotify)
424 event_type = 0; /* ignore other events */
426 if (data->mapped && (!(new_style & WS_VISIBLE) ||
427 (!event_type && !X11DRV_is_window_rect_mapped( rectWindow ))))
428 unmap_window( display, data );
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 );
435 if ((new_style & WS_VISIBLE) &&
436 ((new_style & WS_MINIMIZE) || X11DRV_is_window_rect_mapped( rectWindow )))
438 if (!data->mapped || (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)))
439 X11DRV_set_wm_hints( display, data );
441 if (!data->mapped)
443 map_window( display, data, new_style );
445 else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE)))
447 data->iconic = (new_style & WS_MINIMIZE) != 0;
448 TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic );
449 wine_tsx11_lock();
450 if (data->iconic)
451 XIconifyWindow( display, data->whole_window, DefaultScreen(display) );
452 else if (X11DRV_is_window_rect_mapped( rectWindow ))
453 XMapWindow( display, data->whole_window );
454 wine_tsx11_unlock();
455 update_net_wm_states( display, data );
457 else if (!event_type)
459 update_net_wm_states( display, data );
463 wine_tsx11_lock();
464 XFlush( display ); /* make sure changes are done before we start painting again */
465 wine_tsx11_unlock();
469 struct desktop_resize_data
471 RECT old_screen_rect;
472 RECT old_virtual_rect;
475 static BOOL CALLBACK update_windows_on_desktop_resize( HWND hwnd, LPARAM lparam )
477 struct x11drv_win_data *data;
478 Display *display = thread_display();
479 struct desktop_resize_data *resize_data = (struct desktop_resize_data *)lparam;
480 int mask = 0;
482 if (!(data = X11DRV_get_win_data( hwnd ))) return TRUE;
484 if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
486 /* update the full screen state */
487 update_net_wm_states( display, data );
490 if (resize_data->old_virtual_rect.left != virtual_screen_rect.left) mask |= CWX;
491 if (resize_data->old_virtual_rect.top != virtual_screen_rect.top) mask |= CWY;
492 if (mask && data->whole_window)
494 XWindowChanges changes;
496 wine_tsx11_lock();
497 changes.x = data->whole_rect.left - virtual_screen_rect.left;
498 changes.y = data->whole_rect.top - virtual_screen_rect.top;
499 XReconfigureWMWindow( display, data->whole_window,
500 DefaultScreen(display), mask, &changes );
501 wine_tsx11_unlock();
503 return TRUE;
507 /***********************************************************************
508 * X11DRV_resize_desktop
510 void X11DRV_resize_desktop( unsigned int width, unsigned int height )
512 HWND hwnd = GetDesktopWindow();
513 struct desktop_resize_data resize_data;
515 SetRect( &resize_data.old_screen_rect, 0, 0, screen_width, screen_height );
516 resize_data.old_virtual_rect = virtual_screen_rect;
518 xinerama_init( width, height );
520 if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId())
522 SendMessageW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, MAKELPARAM( width, height ) );
524 else
526 TRACE( "desktop %p change to (%dx%d)\n", hwnd, width, height );
527 SetWindowPos( hwnd, 0, virtual_screen_rect.left, virtual_screen_rect.top,
528 virtual_screen_rect.right - virtual_screen_rect.left,
529 virtual_screen_rect.bottom - virtual_screen_rect.top,
530 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE );
531 SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp,
532 MAKELPARAM( width, height ), SMTO_ABORTIFHUNG, 2000, NULL );
535 EnumWindows( update_windows_on_desktop_resize, (LPARAM)&resize_data );
539 /***********************************************************************
540 * X11DRV_ConfigureNotify
542 void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
544 XConfigureEvent *event = &xev->xconfigure;
545 struct x11drv_win_data *data;
546 RECT rect;
547 UINT flags;
548 int cx, cy, x = event->x, y = event->y;
550 if (!hwnd) return;
551 if (!(data = X11DRV_get_win_data( hwnd ))) return;
552 if (!data->mapped || data->iconic) return;
554 /* Get geometry */
556 if (!event->send_event) /* normal event, need to map coordinates to the root */
558 Window child;
559 wine_tsx11_lock();
560 XTranslateCoordinates( event->display, data->whole_window, root_window,
561 0, 0, &x, &y, &child );
562 wine_tsx11_unlock();
564 rect.left = x;
565 rect.top = y;
566 rect.right = x + event->width;
567 rect.bottom = y + event->height;
568 OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
569 TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
570 hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
571 event->x, event->y, event->width, event->height );
572 X11DRV_X_to_window_rect( data, &rect );
574 x = rect.left;
575 y = rect.top;
576 cx = rect.right - rect.left;
577 cy = rect.bottom - rect.top;
578 flags = SWP_NOACTIVATE | SWP_NOZORDER;
580 /* Compare what has changed */
582 GetWindowRect( hwnd, &rect );
583 if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
584 else
585 TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
586 hwnd, rect.left, rect.top, x, y );
588 if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
589 (IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
591 if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
592 flags |= SWP_NOSIZE;
594 else
595 TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
596 hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
598 SetWindowPos( hwnd, 0, x, y, cx, cy, flags );