wine/dlls/winex11.drv/winpos.c

1599 lines
55 KiB
C

/*
* Window position related functions.
*
* Copyright 1993, 1994, 1995, 2001 Alexandre Julliard
* Copyright 1995, 1996, 1999 Alex Korobka
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef HAVE_LIBXSHAPE
#include <X11/extensions/shape.h>
#endif /* HAVE_LIBXSHAPE */
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winerror.h"
#include "wine/wingdi16.h"
#include "x11drv.h"
#include "win.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
#define SWP_AGG_NOPOSCHANGE \
(SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
#define SWP_EX_NOCOPY 0x0001
#define SWP_EX_PAINTSELF 0x0002
#define SWP_EX_NONCLIENT 0x0004
#define HAS_THICKFRAME(style) \
(((style) & WS_THICKFRAME) && \
!(((style) & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME))
#define ON_LEFT_BORDER(hit) \
(((hit) == HTLEFT) || ((hit) == HTTOPLEFT) || ((hit) == HTBOTTOMLEFT))
#define ON_RIGHT_BORDER(hit) \
(((hit) == HTRIGHT) || ((hit) == HTTOPRIGHT) || ((hit) == HTBOTTOMRIGHT))
#define ON_TOP_BORDER(hit) \
(((hit) == HTTOP) || ((hit) == HTTOPLEFT) || ((hit) == HTTOPRIGHT))
#define ON_BOTTOM_BORDER(hit) \
(((hit) == HTBOTTOM) || ((hit) == HTBOTTOMLEFT) || ((hit) == HTBOTTOMRIGHT))
#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
#define _NET_WM_MOVERESIZE_SIZE_TOP 1
#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
static const char managed_prop[] = "__wine_x11_managed";
/***********************************************************************
* X11DRV_Expose
*/
void X11DRV_Expose( HWND hwnd, XEvent *xev )
{
XExposeEvent *event = &xev->xexpose;
RECT rect;
struct x11drv_win_data *data;
int flags = RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN;
TRACE( "win %p (%lx) %d,%d %dx%d\n",
hwnd, event->window, event->x, event->y, event->width, event->height );
if (!(data = X11DRV_get_win_data( hwnd ))) return;
rect.left = data->whole_rect.left + event->x;
rect.top = data->whole_rect.top + event->y;
rect.right = rect.left + event->width;
rect.bottom = rect.top + event->height;
if (event->window == root_window)
OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
if (rect.left < data->client_rect.left ||
rect.top < data->client_rect.top ||
rect.right > data->client_rect.right ||
rect.bottom > data->client_rect.bottom) flags |= RDW_FRAME;
SERVER_START_REQ( update_window_zorder )
{
req->window = hwnd;
req->rect.left = rect.left;
req->rect.top = rect.top;
req->rect.right = rect.right;
req->rect.bottom = rect.bottom;
wine_server_call( req );
}
SERVER_END_REQ;
/* make position relative to client area instead of parent */
OffsetRect( &rect, -data->client_rect.left, -data->client_rect.top );
RedrawWindow( hwnd, &rect, 0, flags );
}
/***********************************************************************
* SetWindowStyle (X11DRV.@)
*
* Update the X state of a window to reflect a style change
*/
void X11DRV_SetWindowStyle( HWND hwnd, DWORD old_style )
{
Display *display = thread_display();
struct x11drv_win_data *data;
DWORD new_style, changed;
if (hwnd == GetDesktopWindow()) return;
if (!(data = X11DRV_get_win_data( hwnd ))) return;
new_style = GetWindowLongW( hwnd, GWL_STYLE );
changed = new_style ^ old_style;
if (changed & WS_VISIBLE)
{
if (data->whole_window && X11DRV_is_window_rect_mapped( &data->window_rect ))
{
if (new_style & WS_VISIBLE)
{
TRACE( "mapping win %p\n", hwnd );
X11DRV_sync_window_style( display, data );
X11DRV_set_wm_hints( display, data );
wine_tsx11_lock();
XMapWindow( display, data->whole_window );
wine_tsx11_unlock();
}
/* we don't unmap windows, that causes trouble with the window manager */
}
invalidate_dce( hwnd, &data->window_rect );
}
if (changed & WS_DISABLED)
{
if (data->whole_window && data->wm_hints)
{
wine_tsx11_lock();
data->wm_hints->input = !(new_style & WS_DISABLED);
XSetWMHints( display, data->whole_window, data->wm_hints );
wine_tsx11_unlock();
}
}
}
/***********************************************************************
* update_wm_states
*/
static void update_wm_states( Display *display, struct x11drv_win_data *data, BOOL force )
{
static const unsigned int state_atoms[NB_WM_STATES] =
{
XATOM__NET_WM_STATE_FULLSCREEN,
XATOM__NET_WM_STATE_ABOVE,
XATOM__NET_WM_STATE_SKIP_PAGER,
XATOM__NET_WM_STATE_SKIP_TASKBAR
};
DWORD i, ex_style, new_state = 0;
XEvent xev;
if (!data->managed) return;
if (data->whole_rect.left <= 0 && data->whole_rect.right >= screen_width &&
data->whole_rect.top <= 0 && data->whole_rect.bottom >= screen_height)
new_state |= (1 << WM_STATE_FULLSCREEN);
ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
if (ex_style & WS_EX_TOPMOST)
new_state |= (1 << WM_STATE_ABOVE);
if (ex_style & WS_EX_TOOLWINDOW)
new_state |= (1 << WM_STATE_SKIP_TASKBAR) | (1 << WM_STATE_SKIP_PAGER);
xev.xclient.type = ClientMessage;
xev.xclient.window = data->whole_window;
xev.xclient.message_type = x11drv_atom(_NET_WM_STATE);
xev.xclient.serial = 0;
xev.xclient.display = display;
xev.xclient.send_event = True;
xev.xclient.format = 32;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 1;
for (i = 0; i < NB_WM_STATES; i++)
{
if (!((data->wm_state ^ new_state) & (1 << i))) /* unchanged */
{
/* if forced, we only add states, we don't remove them */
if (!force || !(new_state & (1 << i))) continue;
}
TRACE( "setting wm state %u for window %p/%lx to %u prev %u\n",
i, data->hwnd, data->whole_window,
(new_state & (1 << i)) != 0, (data->wm_state & (1 << i)) != 0 );
xev.xclient.data.l[0] = (new_state & (1 << i)) ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xev.xclient.data.l[1] = X11DRV_Atoms[state_atoms[i] - FIRST_XATOM];
wine_tsx11_lock();
XSendEvent( display, root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
wine_tsx11_unlock();
}
data->wm_state = new_state;
}
/***********************************************************************
* move_window_bits
*
* Move the window bits when a window is moved.
*/
static void move_window_bits( struct x11drv_win_data *data, const RECT *old_rect, const RECT *new_rect,
const RECT *window_rect, const RECT *whole_rect, const RECT *client_rect )
{
RECT src_rect = *old_rect;
RECT dst_rect = *new_rect;
HDC hdc_src, hdc_dst;
INT code;
HRGN rgn = 0;
HWND parent = 0;
if (!data->whole_window)
{
OffsetRect( &dst_rect, -window_rect->left, -window_rect->top );
parent = GetAncestor( data->hwnd, GA_PARENT );
hdc_src = GetDCEx( parent, 0, DCX_CACHE );
hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE | DCX_WINDOW );
}
else
{
OffsetRect( &dst_rect, -whole_rect->left, -whole_rect->top );
/* make src rect relative to the old position of the window */
OffsetRect( &src_rect, -data->whole_rect.left, -data->whole_rect.top );
if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
/* now make them relative to window rect for DCX_WINDOW */
OffsetRect( &dst_rect, whole_rect->left - window_rect->left,
whole_rect->top - window_rect->top );
OffsetRect( &src_rect, whole_rect->left - window_rect->left,
whole_rect->top - window_rect->top );
hdc_src = hdc_dst = GetDCEx( data->hwnd, 0, DCX_CACHE | DCX_WINDOW );
}
code = X11DRV_START_EXPOSURES;
ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL );
TRACE( "copying bits for win %p/%lx %s -> %s\n",
data->hwnd, data->whole_window, wine_dbgstr_rect(&src_rect), wine_dbgstr_rect(&dst_rect) );
BitBlt( hdc_dst, dst_rect.left, dst_rect.top,
dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top,
hdc_src, src_rect.left, src_rect.top, SRCCOPY );
code = X11DRV_END_EXPOSURES;
ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(rgn), (LPSTR)&rgn );
ReleaseDC( data->hwnd, hdc_dst );
if (hdc_src != hdc_dst) ReleaseDC( parent, hdc_src );
if (rgn)
{
OffsetRgn( rgn, window_rect->left - client_rect->left, window_rect->top - client_rect->top );
RedrawWindow( data->hwnd, NULL, rgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN );
DeleteObject( rgn );
}
}
/***********************************************************************
* set_server_window_pos
*
* Set the window pos on the server side only. Helper for SetWindowPos.
*/
static BOOL set_server_window_pos( HWND hwnd, HWND insert_after, const RECT *rectWindow,
const RECT *rectClient, UINT swp_flags, const RECT *valid_rects,
RECT *visible_rect )
{
WND *win;
BOOL ret;
if (!(win = WIN_GetPtr( hwnd ))) return FALSE;
if (win == WND_DESKTOP || win == WND_OTHER_PROCESS) return FALSE;
SERVER_START_REQ( set_window_pos )
{
req->handle = hwnd;
req->previous = insert_after;
req->flags = swp_flags;
req->window.left = rectWindow->left;
req->window.top = rectWindow->top;
req->window.right = rectWindow->right;
req->window.bottom = rectWindow->bottom;
req->client.left = rectClient->left;
req->client.top = rectClient->top;
req->client.right = rectClient->right;
req->client.bottom = rectClient->bottom;
if (!IsRectEmpty( &valid_rects[0] ))
wine_server_add_data( req, valid_rects, 2 * sizeof(*valid_rects) );
if ((ret = !wine_server_call( req )))
{
win->dwStyle = reply->new_style;
win->dwExStyle = reply->new_ex_style;
win->rectWindow = *rectWindow;
win->rectClient = *rectClient;
visible_rect->left = reply->visible.left;
visible_rect->top = reply->visible.top;
visible_rect->right = reply->visible.right;
visible_rect->bottom = reply->visible.bottom;
}
}
SERVER_END_REQ;
WIN_ReleasePtr( win );
return ret;
}
/***********************************************************************
* SetWindowPos (X11DRV.@)
*/
BOOL X11DRV_SetWindowPos( HWND hwnd, HWND insert_after, const RECT *rectWindow,
const RECT *rectClient, UINT swp_flags, const RECT *valid_rects )
{
Display *display = thread_display();
struct x11drv_win_data *data;
RECT new_whole_rect, visible_rect;
DWORD old_style, new_style;
BOOL ret, make_managed = FALSE;
if (!(data = X11DRV_get_win_data( hwnd ))) return FALSE;
/* check if we need to switch the window to managed */
if (!data->managed && data->whole_window && managed_mode &&
root_window == DefaultRootWindow( display ) &&
data->whole_window != root_window)
{
if (is_window_managed( hwnd, swp_flags, rectWindow ))
{
TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
make_managed = TRUE;
data->managed = TRUE;
SetPropA( hwnd, managed_prop, (HANDLE)1 );
}
}
old_style = GetWindowLongW( hwnd, GWL_STYLE );
if ((ret = set_server_window_pos( hwnd, insert_after, rectWindow, rectClient, swp_flags,
valid_rects, &visible_rect )))
{
if (data->whole_window == DefaultRootWindow(gdi_display))
{
data->whole_rect = data->client_rect = data->window_rect = *rectWindow;
return TRUE;
}
new_style = GetWindowLongW( hwnd, GWL_STYLE );
new_whole_rect = *rectWindow;
X11DRV_window_to_X_rect( data, &new_whole_rect );
if (memcmp( &visible_rect, &new_whole_rect, sizeof(RECT) ))
{
TRACE( "%p: need to update visible rect %s -> %s\n", hwnd,
wine_dbgstr_rect(&visible_rect), wine_dbgstr_rect(&new_whole_rect) );
SERVER_START_REQ( set_window_visible_rect )
{
req->handle = hwnd;
req->flags = swp_flags;
req->visible.left = new_whole_rect.left;
req->visible.top = new_whole_rect.top;
req->visible.right = new_whole_rect.right;
req->visible.bottom = new_whole_rect.bottom;
wine_server_call( req );
}
SERVER_END_REQ;
}
/* invalidate DCEs */
if ((((swp_flags & SWP_AGG_NOPOSCHANGE) != SWP_AGG_NOPOSCHANGE) && (new_style & WS_VISIBLE)) ||
(swp_flags & (SWP_HIDEWINDOW | SWP_SHOWWINDOW)))
{
RECT rect;
UnionRect( &rect, rectWindow, &data->window_rect );
invalidate_dce( hwnd, &rect );
}
TRACE( "win %p window %s client %s style %08x\n",
hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style );
if (make_managed && (old_style & WS_VISIBLE))
{
wine_tsx11_lock();
XUnmapWindow( display, data->whole_window );
wine_tsx11_unlock();
old_style &= ~WS_VISIBLE; /* force it to be mapped again below */
}
if (!IsRectEmpty( &valid_rects[0] ))
{
int x_offset = data->whole_rect.left - new_whole_rect.left;
int y_offset = data->whole_rect.top - new_whole_rect.top;
/* if all that happened is that the whole window moved, copy everything */
if (!(swp_flags & SWP_FRAMECHANGED) &&
data->whole_rect.right - new_whole_rect.right == x_offset &&
data->whole_rect.bottom - new_whole_rect.bottom == y_offset &&
data->client_rect.left - rectClient->left == x_offset &&
data->client_rect.right - rectClient->right == x_offset &&
data->client_rect.top - rectClient->top == y_offset &&
data->client_rect.bottom - rectClient->bottom == y_offset &&
!memcmp( &valid_rects[0], rectClient, sizeof(RECT) ))
{
/* if we have an X window the bits will be moved by the X server */
if (!data->whole_window)
move_window_bits( data, &data->whole_rect, &new_whole_rect,
rectWindow, &new_whole_rect, rectClient );
}
else
move_window_bits( data, &valid_rects[1], &valid_rects[0],
rectWindow, &new_whole_rect, rectClient );
}
if (data->whole_window && !data->lock_changes)
{
if ((old_style & WS_VISIBLE) && !(new_style & WS_VISIBLE))
{
/* window got hidden, unmap it */
TRACE( "unmapping win %p\n", hwnd );
wine_tsx11_lock();
XUnmapWindow( display, data->whole_window );
wine_tsx11_unlock();
}
else if ((new_style & WS_VISIBLE) && !X11DRV_is_window_rect_mapped( rectWindow ))
{
/* resizing to zero size or off screen -> unmap */
TRACE( "unmapping zero size or off-screen win %p\n", hwnd );
wine_tsx11_lock();
XUnmapWindow( display, data->whole_window );
wine_tsx11_unlock();
}
}
data->window_rect = *rectWindow;
X11DRV_sync_window_position( display, data, swp_flags, rectClient, &new_whole_rect );
if (data->whole_window && !data->lock_changes)
{
BOOL mapped = FALSE;
if ((new_style & WS_VISIBLE) && !(new_style & WS_MINIMIZE) &&
X11DRV_is_window_rect_mapped( rectWindow ))
{
if (!(old_style & WS_VISIBLE))
{
/* window got shown, map it */
TRACE( "mapping win %p\n", hwnd );
mapped = TRUE;
}
else if ((swp_flags & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE))
{
/* resizing from zero size to non-zero -> map */
TRACE( "mapping non zero size or off-screen win %p\n", hwnd );
mapped = TRUE;
}
if (mapped || (swp_flags & SWP_FRAMECHANGED))
X11DRV_set_wm_hints( display, data );
if (mapped)
{
X11DRV_sync_window_style( display, data );
wine_tsx11_lock();
XMapWindow( display, data->whole_window );
XFlush( display );
wine_tsx11_unlock();
}
update_wm_states( display, data, mapped );
}
}
}
return ret;
}
/***********************************************************************
* WINPOS_FindIconPos
*
* Find a suitable place for an iconic window.
*/
static POINT WINPOS_FindIconPos( HWND hwnd, POINT pt )
{
RECT rect, rectParent;
HWND parent, child;
HRGN hrgn, tmp;
int xspacing, yspacing;
parent = GetAncestor( hwnd, GA_PARENT );
GetClientRect( parent, &rectParent );
if ((pt.x >= rectParent.left) && (pt.x + GetSystemMetrics(SM_CXICON) < rectParent.right) &&
(pt.y >= rectParent.top) && (pt.y + GetSystemMetrics(SM_CYICON) < rectParent.bottom))
return pt; /* The icon already has a suitable position */
xspacing = GetSystemMetrics(SM_CXICONSPACING);
yspacing = GetSystemMetrics(SM_CYICONSPACING);
/* Check if another icon already occupies this spot */
/* FIXME: this is completely inefficient */
hrgn = CreateRectRgn( 0, 0, 0, 0 );
tmp = CreateRectRgn( 0, 0, 0, 0 );
for (child = GetWindow( parent, GW_HWNDFIRST ); child; child = GetWindow( child, GW_HWNDNEXT ))
{
WND *childPtr;
if (child == hwnd) continue;
if ((GetWindowLongW( child, GWL_STYLE ) & (WS_VISIBLE|WS_MINIMIZE)) != (WS_VISIBLE|WS_MINIMIZE))
continue;
if (!(childPtr = WIN_GetPtr( child )) || childPtr == WND_OTHER_PROCESS)
continue;
SetRectRgn( tmp, childPtr->rectWindow.left, childPtr->rectWindow.top,
childPtr->rectWindow.right, childPtr->rectWindow.bottom );
CombineRgn( hrgn, hrgn, tmp, RGN_OR );
WIN_ReleasePtr( childPtr );
}
DeleteObject( tmp );
for (rect.bottom = rectParent.bottom; rect.bottom >= yspacing; rect.bottom -= yspacing)
{
for (rect.left = rectParent.left; rect.left <= rectParent.right - xspacing; rect.left += xspacing)
{
rect.right = rect.left + xspacing;
rect.top = rect.bottom - yspacing;
if (!RectInRegion( hrgn, &rect ))
{
/* No window was found, so it's OK for us */
pt.x = rect.left + (xspacing - GetSystemMetrics(SM_CXICON)) / 2;
pt.y = rect.top + (yspacing - GetSystemMetrics(SM_CYICON)) / 2;
DeleteObject( hrgn );
return pt;
}
}
}
DeleteObject( hrgn );
pt.x = pt.y = 0;
return pt;
}
/***********************************************************************
* WINPOS_MinMaximize
*/
UINT WINPOS_MinMaximize( HWND hwnd, UINT cmd, LPRECT rect )
{
WND *wndPtr;
UINT swpFlags = 0;
POINT size;
LONG old_style;
WINDOWPLACEMENT wpl;
TRACE("%p %u\n", hwnd, cmd );
wpl.length = sizeof(wpl);
GetWindowPlacement( hwnd, &wpl );
if (HOOK_CallHooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, TRUE ))
return SWP_NOSIZE | SWP_NOMOVE;
if (IsIconic( hwnd ))
{
switch (cmd)
{
case SW_SHOWMINNOACTIVE:
case SW_SHOWMINIMIZED:
case SW_FORCEMINIMIZE:
case SW_MINIMIZE:
return SWP_NOSIZE | SWP_NOMOVE;
}
if (!SendMessageW( hwnd, WM_QUERYOPEN, 0, 0 )) return SWP_NOSIZE | SWP_NOMOVE;
swpFlags |= SWP_NOCOPYBITS;
}
switch( cmd )
{
case SW_SHOWMINNOACTIVE:
case SW_SHOWMINIMIZED:
case SW_FORCEMINIMIZE:
case SW_MINIMIZE:
if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0;
if( wndPtr->dwStyle & WS_MAXIMIZE) wndPtr->flags |= WIN_RESTORE_MAX;
else wndPtr->flags &= ~WIN_RESTORE_MAX;
WIN_ReleasePtr( wndPtr );
old_style = WIN_SetStyle( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
X11DRV_set_iconic_state( hwnd );
wpl.ptMinPosition = WINPOS_FindIconPos( hwnd, wpl.ptMinPosition );
if (!(old_style & WS_MINIMIZE)) swpFlags |= SWP_STATECHANGED;
SetRect( rect, wpl.ptMinPosition.x, wpl.ptMinPosition.y,
GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON) );
swpFlags |= SWP_NOCOPYBITS;
break;
case SW_MAXIMIZE:
old_style = GetWindowLongW( hwnd, GWL_STYLE );
if ((old_style & WS_MAXIMIZE) && (old_style & WS_VISIBLE)) return SWP_NOSIZE | SWP_NOMOVE;
WINPOS_GetMinMaxInfo( hwnd, &size, &wpl.ptMaxPosition, NULL, NULL );
old_style = WIN_SetStyle( hwnd, WS_MAXIMIZE, WS_MINIMIZE );
if (old_style & WS_MINIMIZE)
{
WINPOS_ShowIconTitle( hwnd, FALSE );
X11DRV_set_iconic_state( hwnd );
}
if (!(old_style & WS_MAXIMIZE)) swpFlags |= SWP_STATECHANGED;
SetRect( rect, wpl.ptMaxPosition.x, wpl.ptMaxPosition.y, size.x, size.y );
break;
case SW_SHOWNOACTIVATE:
case SW_SHOWNORMAL:
case SW_RESTORE:
old_style = WIN_SetStyle( hwnd, 0, WS_MINIMIZE | WS_MAXIMIZE );
if (old_style & WS_MINIMIZE)
{
BOOL restore_max;
WINPOS_ShowIconTitle( hwnd, FALSE );
X11DRV_set_iconic_state( hwnd );
if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return 0;
restore_max = (wndPtr->flags & WIN_RESTORE_MAX) != 0;
WIN_ReleasePtr( wndPtr );
if (restore_max)
{
/* Restore to maximized position */
WINPOS_GetMinMaxInfo( hwnd, &size, &wpl.ptMaxPosition, NULL, NULL);
WIN_SetStyle( hwnd, WS_MAXIMIZE, 0 );
swpFlags |= SWP_STATECHANGED;
SetRect( rect, wpl.ptMaxPosition.x, wpl.ptMaxPosition.y, size.x, size.y );
break;
}
}
else if (!(old_style & WS_MAXIMIZE)) break;
swpFlags |= SWP_STATECHANGED;
/* Restore to normal position */
*rect = wpl.rcNormalPosition;
rect->right -= rect->left;
rect->bottom -= rect->top;
break;
}
return swpFlags;
}
/***********************************************************************
* ShowWindow (X11DRV.@)
*/
BOOL X11DRV_ShowWindow( HWND hwnd, INT cmd )
{
WND *wndPtr;
HWND parent;
LONG style = GetWindowLongW( hwnd, GWL_STYLE );
BOOL wasVisible = (style & WS_VISIBLE) != 0;
BOOL showFlag = TRUE;
RECT newPos = {0, 0, 0, 0};
UINT swp = 0;
TRACE("hwnd=%p, cmd=%d, wasVisible %d\n", hwnd, cmd, wasVisible);
switch(cmd)
{
case SW_HIDE:
if (!wasVisible) return FALSE;
showFlag = FALSE;
swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
if (hwnd != GetActiveWindow())
swp |= SWP_NOACTIVATE | SWP_NOZORDER;
break;
case SW_SHOWMINNOACTIVE:
swp |= SWP_NOACTIVATE | SWP_NOZORDER;
/* fall through */
case SW_MINIMIZE:
case SW_FORCEMINIMIZE: /* FIXME: Does not work if thread is hung. */
if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
/* fall through */
case SW_SHOWMINIMIZED:
swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
swp |= WINPOS_MinMaximize( hwnd, cmd, &newPos );
if ((style & WS_MINIMIZE) && wasVisible) return TRUE;
break;
case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
if (!wasVisible) swp |= SWP_SHOWWINDOW;
swp |= SWP_FRAMECHANGED;
swp |= WINPOS_MinMaximize( hwnd, SW_MAXIMIZE, &newPos );
if ((style & WS_MAXIMIZE) && wasVisible) return TRUE;
break;
case SW_SHOWNA:
swp |= SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
if (style & WS_CHILD) swp |= SWP_NOZORDER;
break;
case SW_SHOW:
if (wasVisible) return TRUE;
swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
break;
case SW_SHOWNOACTIVATE:
swp |= SWP_NOACTIVATE | SWP_NOZORDER;
/* fall through */
case SW_RESTORE:
/* fall through */
case SW_SHOWNORMAL: /* same as SW_NORMAL: */
case SW_SHOWDEFAULT: /* FIXME: should have its own handler */
if (!wasVisible) swp |= SWP_SHOWWINDOW;
if (style & (WS_MINIMIZE | WS_MAXIMIZE))
{
swp |= SWP_FRAMECHANGED;
swp |= WINPOS_MinMaximize( hwnd, cmd, &newPos );
}
else
{
if (wasVisible) return TRUE;
swp |= SWP_NOSIZE | SWP_NOMOVE;
}
if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
break;
}
if ((showFlag != wasVisible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
{
SendMessageW( hwnd, WM_SHOWWINDOW, showFlag, 0 );
if (!IsWindow( hwnd )) return wasVisible;
}
parent = GetAncestor( hwnd, GA_PARENT );
if (parent && !IsWindowVisible( parent ) && !(swp & SWP_STATECHANGED))
{
/* if parent is not visible simply toggle WS_VISIBLE and return */
if (showFlag) WIN_SetStyle( hwnd, WS_VISIBLE, 0 );
else WIN_SetStyle( hwnd, 0, WS_VISIBLE );
}
else
SetWindowPos( hwnd, HWND_TOP, newPos.left, newPos.top,
newPos.right, newPos.bottom, LOWORD(swp) );
if (cmd == SW_HIDE)
{
HWND hFocus;
/* FIXME: This will cause the window to be activated irrespective
* of whether it is owned by the same thread. Has to be done
* asynchronously.
*/
if (hwnd == GetActiveWindow())
WINPOS_ActivateOtherWindow(hwnd);
/* Revert focus to parent */
hFocus = GetFocus();
if (hwnd == hFocus || IsChild(hwnd, hFocus))
{
HWND parent = GetAncestor(hwnd, GA_PARENT);
if (parent == GetDesktopWindow()) parent = 0;
SetFocus(parent);
}
}
if (IsIconic(hwnd)) WINPOS_ShowIconTitle( hwnd, TRUE );
if (!(wndPtr = WIN_GetPtr( hwnd )) || wndPtr == WND_OTHER_PROCESS) return wasVisible;
if (wndPtr->flags & WIN_NEED_SIZE)
{
/* should happen only in CreateWindowEx() */
int wParam = SIZE_RESTORED;
RECT client = wndPtr->rectClient;
wndPtr->flags &= ~WIN_NEED_SIZE;
if (wndPtr->dwStyle & WS_MAXIMIZE) wParam = SIZE_MAXIMIZED;
else if (wndPtr->dwStyle & WS_MINIMIZE) wParam = SIZE_MINIMIZED;
WIN_ReleasePtr( wndPtr );
SendMessageW( hwnd, WM_SIZE, wParam,
MAKELONG( client.right - client.left, client.bottom - client.top ));
SendMessageW( hwnd, WM_MOVE, 0, MAKELONG( client.left, client.top ));
}
else WIN_ReleasePtr( wndPtr );
/* if previous state was minimized Windows sets focus to the window */
if (style & WS_MINIMIZE) SetFocus( hwnd );
return wasVisible;
}
/**********************************************************************
* X11DRV_MapNotify
*/
void X11DRV_MapNotify( HWND hwnd, XEvent *event )
{
struct x11drv_win_data *data;
HWND hwndFocus = GetFocus();
WND *win;
if (!(data = X11DRV_get_win_data( hwnd ))) return;
if (!(win = WIN_GetPtr( hwnd ))) return;
if (data->managed && (win->dwStyle & WS_VISIBLE) && (win->dwStyle & WS_MINIMIZE))
{
int x, y;
unsigned int width, height, border, depth;
Window root, top;
RECT rect;
LONG style = WS_VISIBLE;
/* FIXME: hack */
wine_tsx11_lock();
XGetGeometry( event->xmap.display, data->whole_window, &root, &x, &y, &width, &height,
&border, &depth );
XTranslateCoordinates( event->xmap.display, data->whole_window, root, 0, 0, &x, &y, &top );
wine_tsx11_unlock();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
X11DRV_X_to_window_rect( data, &rect );
invalidate_dce( hwnd, &data->window_rect );
if (win->flags & WIN_RESTORE_MAX) style |= WS_MAXIMIZE;
WIN_SetStyle( hwnd, style, WS_MINIMIZE );
WIN_ReleasePtr( win );
SendMessageW( hwnd, WM_SHOWWINDOW, SW_RESTORE, 0 );
data->lock_changes++;
SetWindowPos( hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_STATECHANGED );
data->lock_changes--;
}
else WIN_ReleasePtr( win );
if (hwndFocus && IsChild( hwnd, hwndFocus )) X11DRV_SetFocus(hwndFocus); /* FIXME */
}
/**********************************************************************
* X11DRV_UnmapNotify
*/
void X11DRV_UnmapNotify( HWND hwnd, XEvent *event )
{
struct x11drv_win_data *data;
WND *win;
if (!(data = X11DRV_get_win_data( hwnd ))) return;
if (!(win = WIN_GetPtr( hwnd ))) return;
if ((win->dwStyle & WS_VISIBLE) && data->managed &&
X11DRV_is_window_rect_mapped( &win->rectWindow ))
{
if (win->dwStyle & WS_MAXIMIZE)
win->flags |= WIN_RESTORE_MAX;
else if (!(win->dwStyle & WS_MINIMIZE))
win->flags &= ~WIN_RESTORE_MAX;
WIN_SetStyle( hwnd, WS_MINIMIZE, WS_MAXIMIZE );
WIN_ReleasePtr( win );
EndMenu();
SendMessageW( hwnd, WM_SHOWWINDOW, SW_MINIMIZE, 0 );
data->lock_changes++;
SetWindowPos( hwnd, 0, 0, 0, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_STATECHANGED );
data->lock_changes--;
}
else WIN_ReleasePtr( win );
}
struct desktop_resize_data
{
RECT old_screen_rect;
RECT old_virtual_rect;
};
static BOOL CALLBACK update_windows_on_desktop_resize( HWND hwnd, LPARAM lparam )
{
struct x11drv_win_data *data;
Display *display = thread_display();
struct desktop_resize_data *resize_data = (struct desktop_resize_data *)lparam;
int mask = 0;
if (!(data = X11DRV_get_win_data( hwnd ))) return TRUE;
if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)
{
/* update the full screen state */
update_wm_states( display, data, FALSE );
}
if (resize_data->old_virtual_rect.left != virtual_screen_rect.left) mask |= CWX;
if (resize_data->old_virtual_rect.top != virtual_screen_rect.top) mask |= CWY;
if (mask && data->whole_window)
{
XWindowChanges changes;
wine_tsx11_lock();
changes.x = data->whole_rect.left - virtual_screen_rect.left;
changes.y = data->whole_rect.top - virtual_screen_rect.top;
XReconfigureWMWindow( display, data->whole_window,
DefaultScreen(display), mask, &changes );
wine_tsx11_unlock();
}
return TRUE;
}
/***********************************************************************
* X11DRV_resize_desktop
*/
void X11DRV_resize_desktop( unsigned int width, unsigned int height )
{
HWND hwnd = GetDesktopWindow();
struct x11drv_win_data *data;
struct desktop_resize_data resize_data;
SetRect( &resize_data.old_screen_rect, 0, 0, screen_width, screen_height );
resize_data.old_virtual_rect = virtual_screen_rect;
xinerama_init( width, height );
ClipCursor(NULL);
if (!(data = X11DRV_get_win_data( hwnd )))
{
SendMessageW( hwnd, WM_X11DRV_RESIZE_DESKTOP, 0, MAKELPARAM( width, height ) );
}
else
{
TRACE( "desktop %p change to (%dx%d)\n", hwnd, width, height );
SetWindowPos( hwnd, 0, virtual_screen_rect.left, virtual_screen_rect.top,
virtual_screen_rect.right - virtual_screen_rect.left,
virtual_screen_rect.bottom - virtual_screen_rect.top,
SWP_NOZORDER | SWP_NOACTIVATE );
SendMessageTimeoutW( HWND_BROADCAST, WM_DISPLAYCHANGE, screen_bpp,
MAKELPARAM( width, height ), SMTO_ABORTIFHUNG, 2000, NULL );
}
EnumWindows( update_windows_on_desktop_resize, (LPARAM)&resize_data );
}
/***********************************************************************
* X11DRV_ConfigureNotify
*/
void X11DRV_ConfigureNotify( HWND hwnd, XEvent *xev )
{
XConfigureEvent *event = &xev->xconfigure;
struct x11drv_win_data *data;
RECT rect;
UINT flags;
int cx, cy, x = event->x, y = event->y;
if (!hwnd) return;
if (!(data = X11DRV_get_win_data( hwnd ))) return;
/* Get geometry */
if (!event->send_event) /* normal event, need to map coordinates to the root */
{
Window child;
wine_tsx11_lock();
XTranslateCoordinates( event->display, data->whole_window, root_window,
0, 0, &x, &y, &child );
wine_tsx11_unlock();
}
rect.left = x;
rect.top = y;
rect.right = x + event->width;
rect.bottom = y + event->height;
OffsetRect( &rect, virtual_screen_rect.left, virtual_screen_rect.top );
TRACE( "win %p new X rect %d,%d,%dx%d (event %d,%d,%dx%d)\n",
hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
event->x, event->y, event->width, event->height );
X11DRV_X_to_window_rect( data, &rect );
x = rect.left;
y = rect.top;
cx = rect.right - rect.left;
cy = rect.bottom - rect.top;
flags = SWP_NOACTIVATE | SWP_NOZORDER;
/* Compare what has changed */
GetWindowRect( hwnd, &rect );
if (rect.left == x && rect.top == y) flags |= SWP_NOMOVE;
else
TRACE( "%p moving from (%d,%d) to (%d,%d)\n",
hwnd, rect.left, rect.top, x, y );
if ((rect.right - rect.left == cx && rect.bottom - rect.top == cy) ||
IsIconic(hwnd) ||
(IsRectEmpty( &rect ) && event->width == 1 && event->height == 1))
{
if (flags & SWP_NOMOVE) return; /* if nothing changed, don't do anything */
flags |= SWP_NOSIZE;
}
else
TRACE( "%p resizing from (%dx%d) to (%dx%d)\n",
hwnd, rect.right - rect.left, rect.bottom - rect.top, cx, cy );
data->lock_changes++;
SetWindowPos( hwnd, 0, x, y, cx, cy, flags );
data->lock_changes--;
}
/***********************************************************************
* SetWindowRgn (X11DRV.@)
*
* Assign specified region to window (for non-rectangular windows)
*/
int X11DRV_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
{
struct x11drv_win_data *data;
if (!(data = X11DRV_get_win_data( hwnd )))
{
if (IsWindow( hwnd ))
FIXME( "not supported on other thread window %p\n", hwnd );
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return FALSE;
}
#ifdef HAVE_LIBXSHAPE
if (data->whole_window)
{
Display *display = thread_display();
if (!hrgn)
{
wine_tsx11_lock();
XShapeCombineMask( display, data->whole_window,
ShapeBounding, 0, 0, None, ShapeSet );
wine_tsx11_unlock();
}
else
{
RGNDATA *pRegionData = X11DRV_GetRegionData( hrgn, 0 );
if (pRegionData)
{
wine_tsx11_lock();
XShapeCombineRectangles( display, data->whole_window, ShapeBounding,
data->window_rect.left - data->whole_rect.left,
data->window_rect.top - data->whole_rect.top,
(XRectangle *)pRegionData->Buffer,
pRegionData->rdh.nCount,
ShapeSet, YXBanded );
wine_tsx11_unlock();
HeapFree(GetProcessHeap(), 0, pRegionData);
}
}
}
#endif /* HAVE_LIBXSHAPE */
invalidate_dce( hwnd, &data->window_rect );
return TRUE;
}
/***********************************************************************
* draw_moving_frame
*
* Draw the frame used when moving or resizing window.
*
* FIXME: This causes problems in Win95 mode. (why?)
*/
static void draw_moving_frame( HDC hdc, RECT *rect, BOOL thickframe )
{
if (thickframe)
{
const int width = GetSystemMetrics(SM_CXFRAME);
const int height = GetSystemMetrics(SM_CYFRAME);
HBRUSH hbrush = SelectObject( hdc, GetStockObject( GRAY_BRUSH ) );
PatBlt( hdc, rect->left, rect->top,
rect->right - rect->left - width, height, PATINVERT );
PatBlt( hdc, rect->left, rect->top + height, width,
rect->bottom - rect->top - height, PATINVERT );
PatBlt( hdc, rect->left + width, rect->bottom - 1,
rect->right - rect->left - width, -height, PATINVERT );
PatBlt( hdc, rect->right - 1, rect->top, -width,
rect->bottom - rect->top - height, PATINVERT );
SelectObject( hdc, hbrush );
}
else DrawFocusRect( hdc, rect );
}
/***********************************************************************
* start_size_move
*
* Initialization of a move or resize, when initiated from a menu choice.
* Return hit test code for caption or sizing border.
*/
static LONG start_size_move( HWND hwnd, WPARAM wParam, POINT *capturePoint, LONG style )
{
LONG hittest = 0;
POINT pt;
MSG msg;
RECT rectWindow;
GetWindowRect( hwnd, &rectWindow );
if ((wParam & 0xfff0) == SC_MOVE)
{
/* Move pointer at the center of the caption */
RECT rect = rectWindow;
/* Note: to be exactly centered we should take the different types
* of border into account, but it shouldn't make more than a few pixels
* of difference so let's not bother with that */
rect.top += GetSystemMetrics(SM_CYBORDER);
if (style & WS_SYSMENU)
rect.left += GetSystemMetrics(SM_CXSIZE) + 1;
if (style & WS_MINIMIZEBOX)
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
if (style & WS_MAXIMIZEBOX)
rect.right -= GetSystemMetrics(SM_CXSIZE) + 1;
pt.x = (rect.right + rect.left) / 2;
pt.y = rect.top + GetSystemMetrics(SM_CYSIZE)/2;
hittest = HTCAPTION;
*capturePoint = pt;
}
else /* SC_SIZE */
{
pt.x = pt.y = 0;
while(!hittest)
{
GetMessageW( &msg, 0, WM_KEYFIRST, WM_MOUSELAST );
if (CallMsgFilterW( &msg, MSGF_SIZE )) continue;
switch(msg.message)
{
case WM_MOUSEMOVE:
pt = msg.pt;
hittest = SendMessageW( hwnd, WM_NCHITTEST, 0, MAKELONG( pt.x, pt.y ) );
if ((hittest < HTLEFT) || (hittest > HTBOTTOMRIGHT)) hittest = 0;
break;
case WM_LBUTTONUP:
return 0;
case WM_KEYDOWN:
switch(msg.wParam)
{
case VK_UP:
hittest = HTTOP;
pt.x =(rectWindow.left+rectWindow.right)/2;
pt.y = rectWindow.top + GetSystemMetrics(SM_CYFRAME) / 2;
break;
case VK_DOWN:
hittest = HTBOTTOM;
pt.x =(rectWindow.left+rectWindow.right)/2;
pt.y = rectWindow.bottom - GetSystemMetrics(SM_CYFRAME) / 2;
break;
case VK_LEFT:
hittest = HTLEFT;
pt.x = rectWindow.left + GetSystemMetrics(SM_CXFRAME) / 2;
pt.y =(rectWindow.top+rectWindow.bottom)/2;
break;
case VK_RIGHT:
hittest = HTRIGHT;
pt.x = rectWindow.right - GetSystemMetrics(SM_CXFRAME) / 2;
pt.y =(rectWindow.top+rectWindow.bottom)/2;
break;
case VK_RETURN:
case VK_ESCAPE: return 0;
}
}
}
*capturePoint = pt;
}
SetCursorPos( pt.x, pt.y );
SendMessageW( hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG( hittest, WM_MOUSEMOVE ));
return hittest;
}
/***********************************************************************
* set_movesize_capture
*/
static void set_movesize_capture( HWND hwnd )
{
HWND previous = 0;
SERVER_START_REQ( set_capture_window )
{
req->handle = hwnd;
req->flags = CAPTURE_MOVESIZE;
if (!wine_server_call_err( req ))
{
previous = reply->previous;
hwnd = reply->full_handle;
}
}
SERVER_END_REQ;
if (previous && previous != hwnd)
SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
}
/***********************************************************************
* X11DRV_WMMoveResizeWindow
*
* Tells the window manager to initiate a move or resize operation.
*
* SEE
* http://freedesktop.org/Standards/wm-spec/1.3/ar01s04.html
* or search for "_NET_WM_MOVERESIZE"
*/
static BOOL X11DRV_WMMoveResizeWindow( HWND hwnd, int x, int y, WPARAM wparam )
{
WPARAM syscommand = wparam & 0xfff0;
WPARAM hittest = wparam & 0x0f;
int dir;
XEvent xev;
Display *display = thread_display();
if (syscommand == SC_MOVE)
{
if (!hittest) dir = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
else dir = _NET_WM_MOVERESIZE_MOVE;
}
else
{
/* windows without WS_THICKFRAME are not resizable through the window manager */
if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_THICKFRAME)) return FALSE;
switch (hittest)
{
case WMSZ_LEFT: dir = _NET_WM_MOVERESIZE_SIZE_LEFT; break;
case WMSZ_RIGHT: dir = _NET_WM_MOVERESIZE_SIZE_RIGHT; break;
case WMSZ_TOP: dir = _NET_WM_MOVERESIZE_SIZE_TOP; break;
case WMSZ_TOPLEFT: dir = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; break;
case WMSZ_TOPRIGHT: dir = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; break;
case WMSZ_BOTTOM: dir = _NET_WM_MOVERESIZE_SIZE_BOTTOM; break;
case WMSZ_BOTTOMLEFT: dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; break;
case WMSZ_BOTTOMRIGHT: dir = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; break;
default: dir = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; break;
}
}
TRACE("hwnd %p, x %d, y %d, dir %d\n", hwnd, x, y, dir);
xev.xclient.type = ClientMessage;
xev.xclient.window = X11DRV_get_whole_window(hwnd);
xev.xclient.message_type = x11drv_atom(_NET_WM_MOVERESIZE);
xev.xclient.serial = 0;
xev.xclient.display = display;
xev.xclient.send_event = True;
xev.xclient.format = 32;
xev.xclient.data.l[0] = x; /* x coord */
xev.xclient.data.l[1] = y; /* y coord */
xev.xclient.data.l[2] = dir; /* direction */
xev.xclient.data.l[3] = 1; /* button */
xev.xclient.data.l[4] = 0; /* unused */
/* need to ungrab the pointer that may have been automatically grabbed
* with a ButtonPress event */
wine_tsx11_lock();
XUngrabPointer( display, CurrentTime );
XSendEvent(display, root_window, False, SubstructureNotifyMask, &xev);
wine_tsx11_unlock();
return TRUE;
}
/***********************************************************************
* SysCommandSizeMove (X11DRV.@)
*
* Perform SC_MOVE and SC_SIZE commands.
*/
void X11DRV_SysCommandSizeMove( HWND hwnd, WPARAM wParam )
{
MSG msg;
RECT sizingRect, mouseRect, origRect;
HDC hdc;
HWND parent;
LONG hittest = (LONG)(wParam & 0x0f);
WPARAM syscommand = wParam & 0xfff0;
HCURSOR hDragCursor = 0, hOldCursor = 0;
POINT minTrack, maxTrack;
POINT capturePoint, pt;
LONG style = GetWindowLongA( hwnd, GWL_STYLE );
BOOL thickframe = HAS_THICKFRAME( style );
BOOL iconic = style & WS_MINIMIZE;
BOOL moved = FALSE;
DWORD dwPoint = GetMessagePos ();
BOOL DragFullWindows = TRUE;
Window parent_win, whole_win;
struct x11drv_thread_data *thread_data = x11drv_thread_data();
struct x11drv_win_data *data;
pt.x = (short)LOWORD(dwPoint);
pt.y = (short)HIWORD(dwPoint);
capturePoint = pt;
if (IsZoomed(hwnd) || !IsWindowVisible(hwnd)) return;
if (!(data = X11DRV_get_win_data( hwnd ))) return;
TRACE("hwnd %p (%smanaged), command %04lx, hittest %d, pos %d,%d\n",
hwnd, data->managed ? "" : "NOT ", syscommand, hittest, pt.x, pt.y);
/* if we are managed then we let the WM do all the work */
if (data->managed && X11DRV_WMMoveResizeWindow( hwnd, pt.x, pt.y, wParam )) return;
if (syscommand == SC_MOVE)
{
if (!hittest) hittest = start_size_move( hwnd, wParam, &capturePoint, style );
if (!hittest) return;
}
else /* SC_SIZE */
{
if ( hittest && (syscommand != SC_MOUSEMENU) )
hittest += (HTLEFT - WMSZ_LEFT);
else
{
set_movesize_capture( hwnd );
hittest = start_size_move( hwnd, wParam, &capturePoint, style );
if (!hittest)
{
set_movesize_capture(0);
return;
}
}
}
/* Get min/max info */
WINPOS_GetMinMaxInfo( hwnd, NULL, NULL, &minTrack, &maxTrack );
GetWindowRect( hwnd, &sizingRect );
if (style & WS_CHILD)
{
parent = GetParent(hwnd);
/* make sizing rect relative to parent */
MapWindowPoints( 0, parent, (POINT*)&sizingRect, 2 );
GetClientRect( parent, &mouseRect );
}
else
{
parent = 0;
mouseRect = virtual_screen_rect;
}
origRect = sizingRect;
if (ON_LEFT_BORDER(hittest))
{
mouseRect.left = max( mouseRect.left, sizingRect.right-maxTrack.x );
mouseRect.right = min( mouseRect.right, sizingRect.right-minTrack.x );
}
else if (ON_RIGHT_BORDER(hittest))
{
mouseRect.left = max( mouseRect.left, sizingRect.left+minTrack.x );
mouseRect.right = min( mouseRect.right, sizingRect.left+maxTrack.x );
}
if (ON_TOP_BORDER(hittest))
{
mouseRect.top = max( mouseRect.top, sizingRect.bottom-maxTrack.y );
mouseRect.bottom = min( mouseRect.bottom,sizingRect.bottom-minTrack.y);
}
else if (ON_BOTTOM_BORDER(hittest))
{
mouseRect.top = max( mouseRect.top, sizingRect.top+minTrack.y );
mouseRect.bottom = min( mouseRect.bottom, sizingRect.top+maxTrack.y );
}
if (parent) MapWindowPoints( parent, 0, (LPPOINT)&mouseRect, 2 );
/* Retrieve a default cache DC (without using the window style) */
hdc = GetDCEx( parent, 0, DCX_CACHE );
if( iconic ) /* create a cursor for dragging */
{
hDragCursor = (HCURSOR)GetClassLongPtrW( hwnd, GCLP_HICON);
if( !hDragCursor ) hDragCursor = (HCURSOR)SendMessageW( hwnd, WM_QUERYDRAGICON, 0, 0L);
if( !hDragCursor ) iconic = FALSE;
}
/* repaint the window before moving it around */
RedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
SendMessageW( hwnd, WM_ENTERSIZEMOVE, 0, 0 );
set_movesize_capture( hwnd );
/* we only allow disabling the full window drag for child windows, or in desktop mode */
/* otherwise we'd need to grab the server and we want to avoid that */
if (parent || (root_window != DefaultRootWindow(gdi_display)))
SystemParametersInfoA(SPI_GETDRAGFULLWINDOWS, 0, &DragFullWindows, 0);
whole_win = X11DRV_get_whole_window( GetAncestor(hwnd,GA_ROOT) );
parent_win = parent ? X11DRV_get_whole_window( GetAncestor(parent,GA_ROOT) ) : root_window;
wine_tsx11_lock();
XGrabPointer( thread_data->display, whole_win, False,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, parent_win, None, CurrentTime );
wine_tsx11_unlock();
thread_data->grab_window = whole_win;
while(1)
{
int dx = 0, dy = 0;
if (!GetMessageW( &msg, 0, 0, 0 )) break;
if (CallMsgFilterW( &msg, MSGF_SIZE )) continue;
/* Exit on button-up, Return, or Esc */
if ((msg.message == WM_LBUTTONUP) ||
((msg.message == WM_KEYDOWN) &&
((msg.wParam == VK_RETURN) || (msg.wParam == VK_ESCAPE)))) break;
if ((msg.message != WM_KEYDOWN) && (msg.message != WM_MOUSEMOVE))
{
TranslateMessage( &msg );
DispatchMessageW( &msg );
continue; /* We are not interested in other messages */
}
pt = msg.pt;
if (msg.message == WM_KEYDOWN) switch(msg.wParam)
{
case VK_UP: pt.y -= 8; break;
case VK_DOWN: pt.y += 8; break;
case VK_LEFT: pt.x -= 8; break;
case VK_RIGHT: pt.x += 8; break;
}
pt.x = max( pt.x, mouseRect.left );
pt.x = min( pt.x, mouseRect.right );
pt.y = max( pt.y, mouseRect.top );
pt.y = min( pt.y, mouseRect.bottom );
dx = pt.x - capturePoint.x;
dy = pt.y - capturePoint.y;
if (dx || dy)
{
if( !moved )
{
moved = TRUE;
if( iconic ) /* ok, no system popup tracking */
{
hOldCursor = SetCursor(hDragCursor);
ShowCursor( TRUE );
WINPOS_ShowIconTitle( hwnd, FALSE );
}
else if(!DragFullWindows)
draw_moving_frame( hdc, &sizingRect, thickframe );
}
if (msg.message == WM_KEYDOWN) SetCursorPos( pt.x, pt.y );
else
{
RECT newRect = sizingRect;
WPARAM wpSizingHit = 0;
if (hittest == HTCAPTION) OffsetRect( &newRect, dx, dy );
if (ON_LEFT_BORDER(hittest)) newRect.left += dx;
else if (ON_RIGHT_BORDER(hittest)) newRect.right += dx;
if (ON_TOP_BORDER(hittest)) newRect.top += dy;
else if (ON_BOTTOM_BORDER(hittest)) newRect.bottom += dy;
if(!iconic && !DragFullWindows) draw_moving_frame( hdc, &sizingRect, thickframe );
capturePoint = pt;
/* determine the hit location */
if (hittest >= HTLEFT && hittest <= HTBOTTOMRIGHT)
wpSizingHit = WMSZ_LEFT + (hittest - HTLEFT);
SendMessageW( hwnd, WM_SIZING, wpSizingHit, (LPARAM)&newRect );
if (!iconic)
{
if(!DragFullWindows)
draw_moving_frame( hdc, &newRect, thickframe );
else
SetWindowPos( hwnd, 0, newRect.left, newRect.top,
newRect.right - newRect.left,
newRect.bottom - newRect.top,
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
}
sizingRect = newRect;
}
}
}
set_movesize_capture(0);
if( iconic )
{
if( moved ) /* restore cursors, show icon title later on */
{
ShowCursor( FALSE );
SetCursor( hOldCursor );
}
}
else if (moved && !DragFullWindows)
{
draw_moving_frame( hdc, &sizingRect, thickframe );
/* make sure the moving frame is erased before we move the window */
wine_tsx11_lock();
XFlush( gdi_display );
wine_tsx11_unlock();
}
ReleaseDC( parent, hdc );
wine_tsx11_lock();
XUngrabPointer( thread_data->display, CurrentTime );
wine_tsx11_unlock();
thread_data->grab_window = None;
if (HOOK_CallHooks( WH_CBT, HCBT_MOVESIZE, (WPARAM)hwnd, (LPARAM)&sizingRect, TRUE ))
moved = FALSE;
SendMessageW( hwnd, WM_EXITSIZEMOVE, 0, 0 );
SendMessageW( hwnd, WM_SETVISIBLE, !IsIconic(hwnd), 0L);
/* window moved or resized */
if (moved)
{
/* if the moving/resizing isn't canceled call SetWindowPos
* with the new position or the new size of the window
*/
if (!((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE)) )
{
/* NOTE: SWP_NOACTIVATE prevents document window activation in Word 6 */
if(!DragFullWindows)
SetWindowPos( hwnd, 0, sizingRect.left, sizingRect.top,
sizingRect.right - sizingRect.left,
sizingRect.bottom - sizingRect.top,
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
}
else
{ /* restore previous size/position */
if(DragFullWindows)
SetWindowPos( hwnd, 0, origRect.left, origRect.top,
origRect.right - origRect.left,
origRect.bottom - origRect.top,
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
}
}
if (IsIconic(hwnd))
{
/* Single click brings up the system menu when iconized */
if( !moved )
{
if(style & WS_SYSMENU )
SendMessageW( hwnd, WM_SYSCOMMAND,
SC_MOUSEMENU + HTSYSMENU, MAKELONG(pt.x,pt.y));
}
else WINPOS_ShowIconTitle( hwnd, TRUE );
}
}