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
25 #include <X11/Xutil.h>
33 #include "wine/wingdi16.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];
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
);
105 XChangeProperty( display
, data
->whole_window
, x11drv_atom(_NET_WM_STATE
), XA_ATOM
,
106 32, PropModeReplace
, (unsigned char *)atoms
, count
);
109 else /* ask the window manager to do it for us */
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);
135 XSendEvent( display
, root_window
, False
,
136 SubstructureRedirectMask
| SubstructureNotifyMask
, &xev
);
140 data
->net_wm_state
= new_state
;
144 /***********************************************************************
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 */
154 XChangeProperty( display
, data
->whole_window
, x11drv_atom(_XEMBED_INFO
),
155 x11drv_atom(_XEMBED_INFO
), 32, PropModeReplace
, (unsigned char*)info
, 2 );
160 /***********************************************************************
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
);
171 update_net_wm_states( display
, data
);
172 X11DRV_sync_window_style( display
, data
);
174 XMapWindow( display
, data
->whole_window
);
177 else set_xembed_flags( display
, data
, XEMBED_MAPPED
);
180 data
->iconic
= (new_style
& WS_MINIMIZE
) != 0;
184 /***********************************************************************
187 static void unmap_window( Display
*display
, struct x11drv_win_data
*data
)
189 TRACE( "win %p/%lx\n", data
->hwnd
, data
->whole_window
);
193 wait_for_withdrawn_state( display
, data
, FALSE
);
195 if (data
->managed
) XWithdrawWindow( display
, data
->whole_window
, DefaultScreen(display
) );
196 else XUnmapWindow( display
, data
->whole_window
);
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
)
213 /* the window cannot be mapped before being embedded */
214 unmap_window( display
, data
);
215 data
->embedded
= TRUE
;
216 map_window( display
, data
, 0 );
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
)
260 data
->wm_hints
->input
= !(new_style
& WS_DISABLED
);
261 XSetWMHints( display
, data
->whole_window
, data
->wm_hints
);
268 /***********************************************************************
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
;
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
);
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
);
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
);
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
;
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
)
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
);
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
);
407 move_window_bits( data
, &valid_rects
[1], &valid_rects
[0], &old_client_rect
);
411 XFlush( gdi_display
); /* make sure painting is done before we move the window */
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 */
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 */
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
);
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
);
451 XIconifyWindow( display
, data
->whole_window
, DefaultScreen(display
) );
452 else if (X11DRV_is_window_rect_mapped( rectWindow
))
453 XMapWindow( display
, data
->whole_window
);
455 update_net_wm_states( display
, data
);
457 else if (!event_type
)
459 update_net_wm_states( display
, data
);
464 XFlush( display
); /* make sure changes are done before we start painting again */
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
;
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
;
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
);
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
) );
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
;
548 int cx
, cy
, x
= event
->x
, y
= event
->y
;
551 if (!(data
= X11DRV_get_win_data( hwnd
))) return;
552 if (!data
->mapped
|| data
->iconic
) return;
556 if (!event
->send_event
) /* normal event, need to map coordinates to the root */
560 XTranslateCoordinates( event
->display
, data
->whole_window
, root_window
,
561 0, 0, &x
, &y
, &child
);
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
);
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
;
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 */
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
);