wine/dlls/winex11.drv/window.c
Gabriel Ivăncescu 688fe7068c winex11.drv: Always enable minimize/maximize functions for minimized windows.
A window without a minimize or maximize box can still be programmatically
minimized by the Windows API. However, some WMs will refuse to change the
state and not allow it to be maximized again, despite it being minimized,
due to lacking those functions, thus not sending WM_STATE change notifications
when clicking the minimized window on the taskbar.

Heroes of Might and Magic V does this, for example, when losing focus. It
minimizes itself. When maximizing it by clicking on the taskbar it won't
send a WM_STATE change notification (since it was never changed) and fail
to maximize properly, showing a black screen (the game will still think it
is minimized).

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2020-12-01 13:41:53 +01:00

2951 lines
101 KiB
C

/*
* Window related functions
*
* Copyright 1993, 1994, 1995, 1996, 2001 Alexandre Julliard
* Copyright 1993 David Metcalfe
* Copyright 1995, 1996 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 "wine/port.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xresource.h>
#include <X11/Xutil.h>
#ifdef HAVE_LIBXSHAPE
#include <X11/extensions/shape.h>
#endif /* HAVE_LIBXSHAPE */
/* avoid conflict with field names in included win32 headers */
#undef Status
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "x11drv.h"
#include "wine/debug.h"
#include "wine/server.h"
#include "mwm.h"
WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
#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
#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
static const unsigned int net_wm_state_atoms[NB_NET_WM_STATES] =
{
XATOM__NET_WM_STATE_FULLSCREEN,
XATOM__NET_WM_STATE_ABOVE,
XATOM__NET_WM_STATE_MAXIMIZED_VERT,
XATOM__NET_WM_STATE_SKIP_PAGER,
XATOM__NET_WM_STATE_SKIP_TASKBAR
};
#define SWP_AGG_NOPOSCHANGE (SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE | SWP_NOZORDER)
/* is cursor clipping active? */
BOOL clipping_cursor = FALSE;
/* X context to associate a hwnd to an X window */
XContext winContext = 0;
/* X context to associate a struct x11drv_win_data to an hwnd */
XContext win_data_context = 0;
/* time of last user event and window where it's stored */
static Time last_user_time;
static Window user_time_window;
static const char foreign_window_prop[] = "__wine_x11_foreign_window";
static const char whole_window_prop[] = "__wine_x11_whole_window";
static const char clip_window_prop[] = "__wine_x11_clip_window";
static CRITICAL_SECTION win_data_section;
static CRITICAL_SECTION_DEBUG critsect_debug =
{
0, 0, &win_data_section,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": win_data_section") }
};
static CRITICAL_SECTION win_data_section = { &critsect_debug, -1, 0, 0, 0, 0 };
/***********************************************************************
* http://standards.freedesktop.org/startup-notification-spec
*/
static void remove_startup_notification(Display *display, Window window)
{
static LONG startup_notification_removed = 0;
char id[1024];
char message[1024];
int i;
int pos;
XEvent xevent;
const char *src;
int srclen;
if (InterlockedCompareExchange(&startup_notification_removed, 1, 0) != 0)
return;
if (GetEnvironmentVariableA("DESKTOP_STARTUP_ID", id, sizeof(id)) == 0)
return;
SetEnvironmentVariableA("DESKTOP_STARTUP_ID", NULL);
if ((src = strstr( id, "_TIME" ))) update_user_time( atol( src + 5 ));
pos = snprintf(message, sizeof(message), "remove: ID=");
message[pos++] = '"';
for (i = 0; id[i] && pos < sizeof(message) - 3; i++)
{
if (id[i] == '"' || id[i] == '\\')
message[pos++] = '\\';
message[pos++] = id[i];
}
message[pos++] = '"';
message[pos++] = '\0';
xevent.xclient.type = ClientMessage;
xevent.xclient.message_type = x11drv_atom(_NET_STARTUP_INFO_BEGIN);
xevent.xclient.display = display;
xevent.xclient.window = window;
xevent.xclient.format = 8;
src = message;
srclen = strlen(src) + 1;
while (srclen > 0)
{
int msglen = srclen;
if (msglen > 20)
msglen = 20;
memset(&xevent.xclient.data.b[0], 0, 20);
memcpy(&xevent.xclient.data.b[0], src, msglen);
src += msglen;
srclen -= msglen;
XSendEvent( display, DefaultRootWindow( display ), False, PropertyChangeMask, &xevent );
xevent.xclient.message_type = x11drv_atom(_NET_STARTUP_INFO);
}
}
struct has_popup_result
{
HWND hwnd;
BOOL found;
};
static BOOL is_managed( HWND hwnd )
{
struct x11drv_win_data *data = get_win_data( hwnd );
BOOL ret = data && data->managed;
release_win_data( data );
return ret;
}
static BOOL CALLBACK has_managed_popup( HWND hwnd, LPARAM lparam )
{
struct has_popup_result *result = (struct has_popup_result *)lparam;
if (hwnd == result->hwnd) return FALSE; /* popups are always above owner */
if (GetWindow( hwnd, GW_OWNER ) != result->hwnd) return TRUE;
result->found = is_managed( hwnd );
return !result->found;
}
static BOOL has_owned_popups( HWND hwnd )
{
struct has_popup_result result;
result.hwnd = hwnd;
result.found = FALSE;
EnumWindows( has_managed_popup, (LPARAM)&result );
return result.found;
}
/***********************************************************************
* alloc_win_data
*/
static struct x11drv_win_data *alloc_win_data( Display *display, HWND hwnd )
{
struct x11drv_win_data *data;
if ((data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data))))
{
data->display = display;
data->vis = default_visual;
data->hwnd = hwnd;
EnterCriticalSection( &win_data_section );
XSaveContext( gdi_display, (XID)hwnd, win_data_context, (char *)data );
}
return data;
}
/***********************************************************************
* is_window_managed
*
* Check if a given window should be managed
*/
static BOOL is_window_managed( HWND hwnd, UINT swp_flags, const RECT *window_rect )
{
DWORD style, ex_style;
if (!managed_mode) return FALSE;
/* child windows are not managed */
style = GetWindowLongW( hwnd, GWL_STYLE );
if ((style & (WS_CHILD|WS_POPUP)) == WS_CHILD) return FALSE;
/* activated windows are managed */
if (!(swp_flags & (SWP_NOACTIVATE|SWP_HIDEWINDOW))) return TRUE;
if (hwnd == GetActiveWindow()) return TRUE;
/* windows with caption are managed */
if ((style & WS_CAPTION) == WS_CAPTION) return TRUE;
/* windows with thick frame are managed */
if (style & WS_THICKFRAME) return TRUE;
if (style & WS_POPUP)
{
HMONITOR hmon;
MONITORINFO mi;
/* popup with sysmenu == caption are managed */
if (style & WS_SYSMENU) return TRUE;
/* full-screen popup windows are managed */
hmon = MonitorFromWindow( hwnd, MONITOR_DEFAULTTOPRIMARY );
mi.cbSize = sizeof( mi );
GetMonitorInfoW( hmon, &mi );
if (window_rect->left <= mi.rcWork.left && window_rect->right >= mi.rcWork.right &&
window_rect->top <= mi.rcWork.top && window_rect->bottom >= mi.rcWork.bottom)
return TRUE;
}
/* application windows are managed */
ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
if (ex_style & WS_EX_APPWINDOW) return TRUE;
/* windows that own popups are managed */
if (has_owned_popups( hwnd )) return TRUE;
/* default: not managed */
return FALSE;
}
/***********************************************************************
* is_window_resizable
*
* Check if window should be made resizable by the window manager
*/
static inline BOOL is_window_resizable( struct x11drv_win_data *data, DWORD style )
{
if (style & WS_THICKFRAME) return TRUE;
/* Metacity needs the window to be resizable to make it fullscreen */
return is_window_rect_full_screen( &data->whole_rect );
}
struct monitor_info
{
const RECT *rect;
BOOL full_screen;
};
static BOOL CALLBACK enum_monitor_proc( HMONITOR monitor, HDC hdc, RECT *monitor_rect, LPARAM lparam )
{
struct monitor_info *info = (struct monitor_info *)lparam;
if (info->rect->left <= monitor_rect->left && info->rect->right >= monitor_rect->right &&
info->rect->top <= monitor_rect->top && info->rect->bottom >= monitor_rect->bottom)
{
info->full_screen = TRUE;
return FALSE;
}
return TRUE;
}
BOOL is_window_rect_full_screen( const RECT *rect )
{
struct monitor_info info = {rect, FALSE};
EnumDisplayMonitors( NULL, NULL, enum_monitor_proc, (LPARAM)&info );
return info.full_screen;
}
/***********************************************************************
* get_mwm_decorations
*/
static unsigned long get_mwm_decorations( struct x11drv_win_data *data,
DWORD style, DWORD ex_style,
const RECT *window_rect,
const RECT *client_rect )
{
unsigned long ret = 0;
if (!decorated_mode) return 0;
if (EqualRect( window_rect, client_rect )) return 0;
if (IsRectEmpty( window_rect )) return 0;
if (data->shaped) return 0;
if (ex_style & WS_EX_TOOLWINDOW) return 0;
if (ex_style & WS_EX_LAYERED) return 0;
if ((style & WS_CAPTION) == WS_CAPTION)
{
ret |= MWM_DECOR_TITLE | MWM_DECOR_BORDER;
if (style & WS_SYSMENU) ret |= MWM_DECOR_MENU;
if (style & WS_MINIMIZEBOX) ret |= MWM_DECOR_MINIMIZE;
if (style & WS_MAXIMIZEBOX) ret |= MWM_DECOR_MAXIMIZE;
}
if (ex_style & WS_EX_DLGMODALFRAME) ret |= MWM_DECOR_BORDER;
else if (style & WS_THICKFRAME) ret |= MWM_DECOR_BORDER | MWM_DECOR_RESIZEH;
else if ((style & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) ret |= MWM_DECOR_BORDER;
return ret;
}
/***********************************************************************
* get_window_attributes
*
* Fill the window attributes structure for an X window.
*/
static int get_window_attributes( struct x11drv_win_data *data, XSetWindowAttributes *attr )
{
attr->override_redirect = !data->managed;
attr->colormap = data->whole_colormap ? data->whole_colormap : default_colormap;
attr->save_under = ((GetClassLongW( data->hwnd, GCL_STYLE ) & CS_SAVEBITS) != 0);
attr->bit_gravity = NorthWestGravity;
attr->backing_store = NotUseful;
attr->border_pixel = 0;
attr->event_mask = (ExposureMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | EnterWindowMask |
KeyPressMask | KeyReleaseMask | FocusChangeMask |
KeymapStateMask | StructureNotifyMask);
if (data->managed) attr->event_mask |= PropertyChangeMask;
return (CWOverrideRedirect | CWSaveUnder | CWColormap | CWBorderPixel |
CWEventMask | CWBitGravity | CWBackingStore);
}
/***********************************************************************
* sync_window_style
*
* Change the X window attributes when the window style has changed.
*/
static void sync_window_style( struct x11drv_win_data *data )
{
if (data->whole_window != root_window)
{
XSetWindowAttributes attr;
int mask = get_window_attributes( data, &attr );
XChangeWindowAttributes( data->display, data->whole_window, mask, &attr );
}
}
/***********************************************************************
* sync_window_region
*
* Update the X11 window region.
*/
static void sync_window_region( struct x11drv_win_data *data, HRGN win_region )
{
#ifdef HAVE_LIBXSHAPE
HRGN hrgn = win_region;
if (!data->whole_window) return;
data->shaped = FALSE;
if (IsRectEmpty( &data->window_rect )) /* set an empty shape */
{
static XRectangle empty_rect;
XShapeCombineRectangles( data->display, data->whole_window, ShapeBounding, 0, 0,
&empty_rect, 1, ShapeSet, YXBanded );
return;
}
if (hrgn == (HRGN)1) /* hack: win_region == 1 means retrieve region from server */
{
if (!(hrgn = CreateRectRgn( 0, 0, 0, 0 ))) return;
if (GetWindowRgn( data->hwnd, hrgn ) == ERROR)
{
DeleteObject( hrgn );
hrgn = 0;
}
}
if (!hrgn)
{
XShapeCombineMask( data->display, data->whole_window, ShapeBounding, 0, 0, None, ShapeSet );
}
else
{
RGNDATA *pRegionData;
if (GetWindowLongW( data->hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) MirrorRgn( data->hwnd, hrgn );
if ((pRegionData = X11DRV_GetRegionData( hrgn, 0 )))
{
XShapeCombineRectangles( data->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 );
HeapFree(GetProcessHeap(), 0, pRegionData);
data->shaped = TRUE;
}
}
if (hrgn && hrgn != win_region) DeleteObject( hrgn );
#endif /* HAVE_LIBXSHAPE */
}
/***********************************************************************
* sync_window_opacity
*/
static void sync_window_opacity( Display *display, Window win,
COLORREF key, BYTE alpha, DWORD flags )
{
unsigned long opacity = 0xffffffff;
if (flags & LWA_ALPHA) opacity = (0xffffffff / 0xff) * alpha;
if (opacity == 0xffffffff)
XDeleteProperty( display, win, x11drv_atom(_NET_WM_WINDOW_OPACITY) );
else
XChangeProperty( display, win, x11drv_atom(_NET_WM_WINDOW_OPACITY),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&opacity, 1 );
}
/***********************************************************************
* sync_window_text
*/
static void sync_window_text( Display *display, Window win, const WCHAR *text )
{
UINT count;
char *buffer, *utf8_buffer;
XTextProperty prop;
/* allocate new buffer for window text */
count = WideCharToMultiByte(CP_UNIXCP, 0, text, -1, NULL, 0, NULL, NULL);
if (!(buffer = HeapAlloc( GetProcessHeap(), 0, count ))) return;
WideCharToMultiByte(CP_UNIXCP, 0, text, -1, buffer, count, NULL, NULL);
count = WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), NULL, 0, NULL, NULL);
if (!(utf8_buffer = HeapAlloc( GetProcessHeap(), 0, count )))
{
HeapFree( GetProcessHeap(), 0, buffer );
return;
}
WideCharToMultiByte(CP_UTF8, 0, text, strlenW(text), utf8_buffer, count, NULL, NULL);
if (XmbTextListToTextProperty( display, &buffer, 1, XStdICCTextStyle, &prop ) == Success)
{
XSetWMName( display, win, &prop );
XSetWMIconName( display, win, &prop );
XFree( prop.value );
}
/*
Implements a NET_WM UTF-8 title. It should be without a trailing \0,
according to the standard
( http://www.pps.jussieu.fr/~jch/software/UTF8_STRING/UTF8_STRING.text ).
*/
XChangeProperty( display, win, x11drv_atom(_NET_WM_NAME), x11drv_atom(UTF8_STRING),
8, PropModeReplace, (unsigned char *) utf8_buffer, count);
HeapFree( GetProcessHeap(), 0, utf8_buffer );
HeapFree( GetProcessHeap(), 0, buffer );
}
/***********************************************************************
* get_bitmap_argb
*
* Return the bitmap bits in ARGB format. Helper for setting icon hints.
*/
static unsigned long *get_bitmap_argb( HDC hdc, HBITMAP color, HBITMAP mask, unsigned int *size )
{
char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
BITMAPINFO *info = (BITMAPINFO *)buffer;
BITMAP bm;
unsigned int *ptr, *bits = NULL;
unsigned char *mask_bits = NULL;
int i, j;
BOOL has_alpha = FALSE;
if (!GetObjectW( color, sizeof(bm), &bm )) return NULL;
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biWidth = bm.bmWidth;
info->bmiHeader.biHeight = -bm.bmHeight;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBitCount = 32;
info->bmiHeader.biCompression = BI_RGB;
info->bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * 4;
info->bmiHeader.biXPelsPerMeter = 0;
info->bmiHeader.biYPelsPerMeter = 0;
info->bmiHeader.biClrUsed = 0;
info->bmiHeader.biClrImportant = 0;
*size = bm.bmWidth * bm.bmHeight + 2;
if (!(bits = HeapAlloc( GetProcessHeap(), 0, *size * sizeof(long) ))) goto failed;
if (!GetDIBits( hdc, color, 0, bm.bmHeight, bits + 2, info, DIB_RGB_COLORS )) goto failed;
bits[0] = bm.bmWidth;
bits[1] = bm.bmHeight;
for (i = 0; i < bm.bmWidth * bm.bmHeight; i++)
if ((has_alpha = (bits[i + 2] & 0xff000000) != 0)) break;
if (!has_alpha)
{
unsigned int width_bytes = (bm.bmWidth + 31) / 32 * 4;
/* generate alpha channel from the mask */
info->bmiHeader.biBitCount = 1;
info->bmiHeader.biSizeImage = width_bytes * bm.bmHeight;
if (!(mask_bits = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto failed;
if (!GetDIBits( hdc, mask, 0, bm.bmHeight, mask_bits, info, DIB_RGB_COLORS )) goto failed;
ptr = bits + 2;
for (i = 0; i < bm.bmHeight; i++)
for (j = 0; j < bm.bmWidth; j++, ptr++)
if (!((mask_bits[i * width_bytes + j / 8] << (j % 8)) & 0x80)) *ptr |= 0xff000000;
HeapFree( GetProcessHeap(), 0, mask_bits );
}
/* convert to array of longs */
if (bits && sizeof(long) > sizeof(int))
for (i = *size - 1; i >= 0; i--) ((unsigned long *)bits)[i] = bits[i];
return (unsigned long *)bits;
failed:
HeapFree( GetProcessHeap(), 0, bits );
HeapFree( GetProcessHeap(), 0, mask_bits );
return NULL;
}
/***********************************************************************
* create_icon_pixmaps
*/
static BOOL create_icon_pixmaps( HDC hdc, const ICONINFO *icon, Pixmap *icon_ret, Pixmap *mask_ret )
{
char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
BITMAPINFO *info = (BITMAPINFO *)buffer;
XVisualInfo vis = default_visual;
struct gdi_image_bits bits;
Pixmap color_pixmap = 0, mask_pixmap = 0;
int lines;
unsigned int i;
bits.ptr = NULL;
bits.free = NULL;
bits.is_copy = TRUE;
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biBitCount = 0;
if (!(lines = GetDIBits( hdc, icon->hbmColor, 0, 0, NULL, info, DIB_RGB_COLORS ))) goto failed;
if (!(bits.ptr = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto failed;
if (!GetDIBits( hdc, icon->hbmColor, 0, lines, bits.ptr, info, DIB_RGB_COLORS )) goto failed;
color_pixmap = create_pixmap_from_image( hdc, &vis, info, &bits, DIB_RGB_COLORS );
HeapFree( GetProcessHeap(), 0, bits.ptr );
bits.ptr = NULL;
if (!color_pixmap) goto failed;
info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info->bmiHeader.biBitCount = 0;
if (!(lines = GetDIBits( hdc, icon->hbmMask, 0, 0, NULL, info, DIB_RGB_COLORS ))) goto failed;
if (!(bits.ptr = HeapAlloc( GetProcessHeap(), 0, info->bmiHeader.biSizeImage ))) goto failed;
if (!GetDIBits( hdc, icon->hbmMask, 0, lines, bits.ptr, info, DIB_RGB_COLORS )) goto failed;
/* invert the mask */
for (i = 0; i < info->bmiHeader.biSizeImage / sizeof(DWORD); i++) ((DWORD *)bits.ptr)[i] ^= ~0u;
vis.depth = 1;
mask_pixmap = create_pixmap_from_image( hdc, &vis, info, &bits, DIB_RGB_COLORS );
HeapFree( GetProcessHeap(), 0, bits.ptr );
bits.ptr = NULL;
if (!mask_pixmap) goto failed;
*icon_ret = color_pixmap;
*mask_ret = mask_pixmap;
return TRUE;
failed:
if (color_pixmap) XFreePixmap( gdi_display, color_pixmap );
HeapFree( GetProcessHeap(), 0, bits.ptr );
return FALSE;
}
/***********************************************************************
* fetch_icon_data
*/
static void fetch_icon_data( HWND hwnd, HICON icon_big, HICON icon_small )
{
struct x11drv_win_data *data;
ICONINFO ii, ii_small;
HDC hDC;
unsigned int size;
unsigned long *bits;
Pixmap icon_pixmap, mask_pixmap;
if (!icon_big)
{
icon_big = (HICON)SendMessageW( hwnd, WM_GETICON, ICON_BIG, 0 );
if (!icon_big) icon_big = (HICON)GetClassLongPtrW( hwnd, GCLP_HICON );
if (!icon_big) icon_big = LoadIconW( 0, (LPWSTR)IDI_WINLOGO );
}
if (!icon_small)
{
icon_small = (HICON)SendMessageW( hwnd, WM_GETICON, ICON_SMALL, 0 );
if (!icon_small) icon_small = (HICON)GetClassLongPtrW( hwnd, GCLP_HICONSM );
}
if (!GetIconInfo(icon_big, &ii)) return;
hDC = CreateCompatibleDC(0);
bits = get_bitmap_argb( hDC, ii.hbmColor, ii.hbmMask, &size );
if (bits && GetIconInfo( icon_small, &ii_small ))
{
unsigned int size_small;
unsigned long *bits_small, *new;
if ((bits_small = get_bitmap_argb( hDC, ii_small.hbmColor, ii_small.hbmMask, &size_small )) &&
(bits_small[0] != bits[0] || bits_small[1] != bits[1])) /* size must be different */
{
if ((new = HeapReAlloc( GetProcessHeap(), 0, bits,
(size + size_small) * sizeof(unsigned long) )))
{
bits = new;
memcpy( bits + size, bits_small, size_small * sizeof(unsigned long) );
size += size_small;
}
}
HeapFree( GetProcessHeap(), 0, bits_small );
DeleteObject( ii_small.hbmColor );
DeleteObject( ii_small.hbmMask );
}
if (!create_icon_pixmaps( hDC, &ii, &icon_pixmap, &mask_pixmap )) icon_pixmap = mask_pixmap = 0;
DeleteObject( ii.hbmColor );
DeleteObject( ii.hbmMask );
DeleteDC(hDC);
if ((data = get_win_data( hwnd )))
{
if (data->icon_pixmap) XFreePixmap( gdi_display, data->icon_pixmap );
if (data->icon_mask) XFreePixmap( gdi_display, data->icon_mask );
HeapFree( GetProcessHeap(), 0, data->icon_bits );
data->icon_pixmap = icon_pixmap;
data->icon_mask = mask_pixmap;
data->icon_bits = bits;
data->icon_size = size;
release_win_data( data );
}
else
{
if (icon_pixmap) XFreePixmap( gdi_display, icon_pixmap );
if (mask_pixmap) XFreePixmap( gdi_display, mask_pixmap );
HeapFree( GetProcessHeap(), 0, bits );
}
}
/***********************************************************************
* set_size_hints
*
* set the window size hints
*/
static void set_size_hints( struct x11drv_win_data *data, DWORD style )
{
XSizeHints* size_hints;
if (!(size_hints = XAllocSizeHints())) return;
size_hints->win_gravity = StaticGravity;
size_hints->flags |= PWinGravity;
/* don't update size hints if window is not in normal state */
if (!(style & (WS_MINIMIZE | WS_MAXIMIZE)))
{
if (data->hwnd != GetDesktopWindow()) /* don't force position of desktop */
{
size_hints->x = data->whole_rect.left;
size_hints->y = data->whole_rect.top;
size_hints->flags |= PPosition;
}
else size_hints->win_gravity = NorthWestGravity;
if (!is_window_resizable( data, style ))
{
size_hints->max_width = data->whole_rect.right - data->whole_rect.left;
size_hints->max_height = data->whole_rect.bottom - data->whole_rect.top;
if (size_hints->max_width <= 0 ||size_hints->max_height <= 0)
size_hints->max_width = size_hints->max_height = 1;
size_hints->min_width = size_hints->max_width;
size_hints->min_height = size_hints->max_height;
size_hints->flags |= PMinSize | PMaxSize;
}
}
XSetWMNormalHints( data->display, data->whole_window, size_hints );
XFree( size_hints );
}
/***********************************************************************
* set_mwm_hints
*/
static void set_mwm_hints( struct x11drv_win_data *data, DWORD style, DWORD ex_style )
{
MwmHints mwm_hints;
if (data->hwnd == GetDesktopWindow())
{
if (is_desktop_fullscreen()) mwm_hints.decorations = 0;
else mwm_hints.decorations = MWM_DECOR_TITLE | MWM_DECOR_BORDER | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE;
mwm_hints.functions = MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE | MWM_FUNC_CLOSE;
}
else
{
mwm_hints.decorations = get_mwm_decorations( data, style, ex_style, &data->window_rect, &data->client_rect );
mwm_hints.functions = MWM_FUNC_MOVE;
if (is_window_resizable( data, style )) mwm_hints.functions |= MWM_FUNC_RESIZE;
if (!(style & WS_DISABLED))
{
if (style & WS_MINIMIZEBOX) mwm_hints.functions |= MWM_FUNC_MINIMIZE;
if (style & WS_MAXIMIZEBOX) mwm_hints.functions |= MWM_FUNC_MAXIMIZE;
if (style & WS_SYSMENU) mwm_hints.functions |= MWM_FUNC_CLOSE;
/* The window can be programmatically minimized even without
a minimize box button. Allow the WM to restore it. */
if (style & WS_MINIMIZE) mwm_hints.functions |= MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE;
}
}
TRACE( "%p setting mwm hints to %lx,%lx (style %x exstyle %x)\n",
data->hwnd, mwm_hints.decorations, mwm_hints.functions, style, ex_style );
mwm_hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS;
mwm_hints.input_mode = 0;
mwm_hints.status = 0;
XChangeProperty( data->display, data->whole_window, x11drv_atom(_MOTIF_WM_HINTS),
x11drv_atom(_MOTIF_WM_HINTS), 32, PropModeReplace,
(unsigned char*)&mwm_hints, sizeof(mwm_hints)/sizeof(long) );
}
/***********************************************************************
* set_style_hints
*/
static void set_style_hints( struct x11drv_win_data *data, DWORD style, DWORD ex_style )
{
Window group_leader = data->whole_window;
HWND owner = GetWindow( data->hwnd, GW_OWNER );
Window owner_win = 0;
XWMHints *wm_hints;
Atom window_type;
if (owner)
{
owner = GetAncestor( owner, GA_ROOT );
owner_win = X11DRV_get_whole_window( owner );
}
if (owner_win)
{
XSetTransientForHint( data->display, data->whole_window, owner_win );
group_leader = owner_win;
}
/* Only use dialog type for owned popups. Metacity allows making fullscreen
* only normal windows, and doesn't handle correctly TRANSIENT_FOR hint for
* dialogs owned by fullscreen windows.
*/
if (((style & WS_POPUP) || (ex_style & WS_EX_DLGMODALFRAME)) && owner)
window_type = x11drv_atom(_NET_WM_WINDOW_TYPE_DIALOG);
else
window_type = x11drv_atom(_NET_WM_WINDOW_TYPE_NORMAL);
XChangeProperty(data->display, data->whole_window, x11drv_atom(_NET_WM_WINDOW_TYPE),
XA_ATOM, 32, PropModeReplace, (unsigned char*)&window_type, 1);
if ((wm_hints = XAllocWMHints()))
{
wm_hints->flags = InputHint | StateHint | WindowGroupHint;
wm_hints->input = !use_take_focus && !(style & WS_DISABLED);
wm_hints->initial_state = (style & WS_MINIMIZE) ? IconicState : NormalState;
wm_hints->window_group = group_leader;
if (data->icon_pixmap)
{
wm_hints->icon_pixmap = data->icon_pixmap;
wm_hints->icon_mask = data->icon_mask;
wm_hints->flags |= IconPixmapHint | IconMaskHint;
}
XSetWMHints( data->display, data->whole_window, wm_hints );
XFree( wm_hints );
}
if (data->icon_bits)
XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON),
XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)data->icon_bits, data->icon_size );
else
XDeleteProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_ICON) );
}
/***********************************************************************
* set_initial_wm_hints
*
* Set the window manager hints that don't change over the lifetime of a window.
*/
static void set_initial_wm_hints( Display *display, Window window )
{
long i;
Atom protocols[3];
Atom dndVersion = WINE_XDND_VERSION;
XClassHint *class_hints;
/* wm protocols */
i = 0;
protocols[i++] = x11drv_atom(WM_DELETE_WINDOW);
protocols[i++] = x11drv_atom(_NET_WM_PING);
if (use_take_focus) protocols[i++] = x11drv_atom(WM_TAKE_FOCUS);
XChangeProperty( display, window, x11drv_atom(WM_PROTOCOLS),
XA_ATOM, 32, PropModeReplace, (unsigned char *)protocols, i );
/* class hints */
if ((class_hints = XAllocClassHint()))
{
class_hints->res_name = process_name;
class_hints->res_class = process_name;
XSetClassHint( display, window, class_hints );
XFree( class_hints );
}
/* set the WM_CLIENT_MACHINE and WM_LOCALE_NAME properties */
XSetWMProperties(display, window, NULL, NULL, NULL, 0, NULL, NULL, NULL);
/* set the pid. together, these properties are needed so the window manager can kill us if we freeze */
i = getpid();
XChangeProperty(display, window, x11drv_atom(_NET_WM_PID),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&i, 1);
XChangeProperty( display, window, x11drv_atom(XdndAware),
XA_ATOM, 32, PropModeReplace, (unsigned char*)&dndVersion, 1 );
update_user_time( 0 ); /* make sure that the user time window exists */
if (user_time_window)
XChangeProperty( display, window, x11drv_atom(_NET_WM_USER_TIME_WINDOW),
XA_WINDOW, 32, PropModeReplace, (unsigned char *)&user_time_window, 1 );
}
/***********************************************************************
* make_owner_managed
*
* If the window is managed, make sure its owner window is too.
*/
static void make_owner_managed( HWND hwnd )
{
HWND owner;
if (!(owner = GetWindow( hwnd, GW_OWNER ))) return;
if (is_managed( owner )) return;
if (!is_managed( hwnd )) return;
SetWindowPos( owner, 0, 0, 0, 0, 0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOREDRAW | SWP_DEFERERASE | SWP_NOSENDCHANGING | SWP_STATECHANGED );
}
/***********************************************************************
* set_wm_hints
*
* Set all the window manager hints for a window.
*/
static void set_wm_hints( struct x11drv_win_data *data )
{
DWORD style, ex_style;
if (data->hwnd == GetDesktopWindow())
{
/* force some styles for the desktop to get the correct decorations */
style = WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
ex_style = WS_EX_APPWINDOW;
}
else
{
style = GetWindowLongW( data->hwnd, GWL_STYLE );
ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
}
set_size_hints( data, style );
set_mwm_hints( data, style, ex_style );
set_style_hints( data, style, ex_style );
}
/***********************************************************************
* init_clip_window
*/
Window init_clip_window(void)
{
struct x11drv_thread_data *data = x11drv_init_thread_data();
if (!data->clip_window &&
(data->clip_window = (Window)GetPropA( GetDesktopWindow(), clip_window_prop )))
{
XSelectInput( data->display, data->clip_window, StructureNotifyMask );
}
return data->clip_window;
}
/***********************************************************************
* update_user_time
*/
void update_user_time( Time time )
{
if (!user_time_window)
{
Window win = XCreateWindow( gdi_display, root_window, -1, -1, 1, 1, 0, CopyFromParent,
InputOnly, CopyFromParent, 0, NULL );
if (InterlockedCompareExchangePointer( (void **)&user_time_window, (void *)win, 0 ))
XDestroyWindow( gdi_display, win );
TRACE( "user time window %lx\n", user_time_window );
}
if (!time) return;
XLockDisplay( gdi_display );
if (!last_user_time || (long)(time - last_user_time) > 0)
{
last_user_time = time;
XChangeProperty( gdi_display, user_time_window, x11drv_atom(_NET_WM_USER_TIME),
XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&time, 1 );
}
XUnlockDisplay( gdi_display );
}
/***********************************************************************
* update_net_wm_states
*/
void update_net_wm_states( struct x11drv_win_data *data )
{
DWORD i, style, ex_style, new_state = 0;
if (!data->managed) return;
if (data->whole_window == root_window) return;
style = GetWindowLongW( data->hwnd, GWL_STYLE );
if (style & WS_MINIMIZE)
new_state |= data->net_wm_state & ((1 << NET_WM_STATE_FULLSCREEN)|(1 << NET_WM_STATE_MAXIMIZED));
if (is_window_rect_full_screen( &data->whole_rect ))
{
if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
new_state |= (1 << NET_WM_STATE_MAXIMIZED);
else if (!(style & WS_MINIMIZE))
new_state |= (1 << NET_WM_STATE_FULLSCREEN);
}
else if (style & WS_MAXIMIZE)
new_state |= (1 << NET_WM_STATE_MAXIMIZED);
ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
if (ex_style & WS_EX_TOPMOST)
new_state |= (1 << NET_WM_STATE_ABOVE);
if (ex_style & (WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE))
new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR) | (1 << NET_WM_STATE_SKIP_PAGER);
if (!(ex_style & WS_EX_APPWINDOW) && GetWindow( data->hwnd, GW_OWNER ))
new_state |= (1 << NET_WM_STATE_SKIP_TASKBAR);
if (!data->mapped) /* set the _NET_WM_STATE atom directly */
{
Atom atoms[NB_NET_WM_STATES+1];
DWORD count;
for (i = count = 0; i < NB_NET_WM_STATES; i++)
{
if (!(new_state & (1 << i))) continue;
TRACE( "setting wm state %u for unmapped window %p/%lx\n",
i, data->hwnd, data->whole_window );
atoms[count++] = X11DRV_Atoms[net_wm_state_atoms[i] - FIRST_XATOM];
if (net_wm_state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT)
atoms[count++] = x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ);
}
XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_STATE), XA_ATOM,
32, PropModeReplace, (unsigned char *)atoms, count );
}
else /* ask the window manager to do it for us */
{
XEvent xev;
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 = data->display;
xev.xclient.send_event = True;
xev.xclient.format = 32;
xev.xclient.data.l[3] = 1;
xev.xclient.data.l[4] = 0;
for (i = 0; i < NB_NET_WM_STATES; i++)
{
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->net_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[net_wm_state_atoms[i] - FIRST_XATOM];
xev.xclient.data.l[2] = ((net_wm_state_atoms[i] == XATOM__NET_WM_STATE_MAXIMIZED_VERT) ?
x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ) : 0);
XSendEvent( data->display, root_window, False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev );
}
}
data->net_wm_state = new_state;
}
/***********************************************************************
* read_net_wm_states
*/
void read_net_wm_states( Display* display, struct x11drv_win_data *data )
{
Atom type, *state;
int format;
unsigned long i, j, count, remaining;
DWORD new_state = 0;
BOOL maximized_horz = FALSE;
if (!data->whole_window) return;
if (!XGetWindowProperty( display, data->whole_window, x11drv_atom(_NET_WM_STATE), 0,
65536/sizeof(CARD32), False, XA_ATOM, &type, &format, &count,
&remaining, (unsigned char **)&state ))
{
if (type == XA_ATOM && format == 32)
{
for (i = 0; i < count; i++)
{
if (state[i] == x11drv_atom(_NET_WM_STATE_MAXIMIZED_HORZ))
maximized_horz = TRUE;
for (j=0; j < NB_NET_WM_STATES; j++)
{
if (state[i] == X11DRV_Atoms[net_wm_state_atoms[j] - FIRST_XATOM])
{
new_state |= 1 << j;
}
}
}
}
XFree( state );
}
if (!maximized_horz)
new_state &= ~(1 << NET_WM_STATE_MAXIMIZED);
data->net_wm_state = new_state;
}
/***********************************************************************
* set_xembed_flags
*/
static void set_xembed_flags( struct x11drv_win_data *data, unsigned long flags )
{
unsigned long info[2];
if (!data->whole_window) return;
info[0] = 0; /* protocol version */
info[1] = flags;
XChangeProperty( data->display, data->whole_window, x11drv_atom(_XEMBED_INFO),
x11drv_atom(_XEMBED_INFO), 32, PropModeReplace, (unsigned char*)info, 2 );
}
/***********************************************************************
* map_window
*/
static void map_window( HWND hwnd, DWORD new_style )
{
struct x11drv_win_data *data;
make_owner_managed( hwnd );
wait_for_withdrawn_state( hwnd, TRUE );
if (!(data = get_win_data( hwnd ))) return;
if (data->whole_window && !data->mapped)
{
TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
remove_startup_notification( data->display, data->whole_window );
set_wm_hints( data );
if (!data->embedded)
{
update_net_wm_states( data );
sync_window_style( data );
XMapWindow( data->display, data->whole_window );
XFlush( data->display );
if (data->surface && data->vis.visualid != default_visual.visualid)
data->surface->funcs->flush( data->surface );
}
else set_xembed_flags( data, XEMBED_MAPPED );
data->mapped = TRUE;
data->iconic = (new_style & WS_MINIMIZE) != 0;
}
release_win_data( data );
}
/***********************************************************************
* unmap_window
*/
static void unmap_window( HWND hwnd )
{
struct x11drv_win_data *data;
wait_for_withdrawn_state( hwnd, FALSE );
if (!(data = get_win_data( hwnd ))) return;
if (data->mapped)
{
TRACE( "win %p/%lx\n", data->hwnd, data->whole_window );
if (data->embedded) set_xembed_flags( data, 0 );
else if (!data->managed) XUnmapWindow( data->display, data->whole_window );
else XWithdrawWindow( data->display, data->whole_window, data->vis.screen );
data->mapped = FALSE;
data->net_wm_state = 0;
}
release_win_data( data );
}
/***********************************************************************
* make_window_embedded
*/
void make_window_embedded( struct x11drv_win_data *data )
{
/* the window cannot be mapped before being embedded */
if (data->mapped)
{
if (!data->managed) XUnmapWindow( data->display, data->whole_window );
else XWithdrawWindow( data->display, data->whole_window, data->vis.screen );
data->net_wm_state = 0;
}
data->embedded = TRUE;
data->managed = TRUE;
sync_window_style( data );
set_xembed_flags( data, (data->mapped || data->embedder) ? XEMBED_MAPPED : 0 );
}
/***********************************************************************
* get_decoration_rect
*/
static void get_decoration_rect( struct x11drv_win_data *data, RECT *rect,
const RECT *window_rect, const RECT *client_rect )
{
DWORD style, ex_style, style_mask = 0, ex_style_mask = 0;
unsigned long decor;
SetRectEmpty( rect );
if (!data->managed) return;
style = GetWindowLongW( data->hwnd, GWL_STYLE );
ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
decor = get_mwm_decorations( data, style, ex_style, window_rect, client_rect );
if (decor & MWM_DECOR_TITLE) style_mask |= WS_CAPTION;
if (decor & MWM_DECOR_BORDER)
{
style_mask |= WS_DLGFRAME | WS_THICKFRAME;
ex_style_mask |= WS_EX_DLGMODALFRAME;
}
AdjustWindowRectEx( rect, style & style_mask, FALSE, ex_style & ex_style_mask );
}
/***********************************************************************
* X11DRV_window_to_X_rect
*
* Convert a rect from client to X window coordinates
*/
static void X11DRV_window_to_X_rect( struct x11drv_win_data *data, RECT *rect,
const RECT *window_rect, const RECT *client_rect )
{
RECT rc;
if (IsRectEmpty( rect )) return;
get_decoration_rect( data, &rc, window_rect, client_rect );
rect->left -= rc.left;
rect->right -= rc.right;
rect->top -= rc.top;
rect->bottom -= rc.bottom;
if (rect->top >= rect->bottom) rect->bottom = rect->top + 1;
if (rect->left >= rect->right) rect->right = rect->left + 1;
}
/***********************************************************************
* X11DRV_X_to_window_rect
*
* Opposite of X11DRV_window_to_X_rect
*/
void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect, int x, int y, int cx, int cy )
{
RECT rc;
get_decoration_rect( data, &rc, &data->window_rect, &data->client_rect );
x += min( data->window_rect.left - data->whole_rect.left, rc.left );
y += min( data->window_rect.top - data->whole_rect.top, rc.top );
cx += max( (data->window_rect.right - data->window_rect.left) -
(data->whole_rect.right - data->whole_rect.left), rc.right - rc.left );
cy += max( (data->window_rect.bottom - data->window_rect.top) -
(data->whole_rect.bottom - data->whole_rect.top), rc.bottom - rc.top );
SetRect( rect, x, y, x + cx, y + cy );
}
/***********************************************************************
* sync_window_position
*
* Synchronize the X window position with the Windows one
*/
static void sync_window_position( struct x11drv_win_data *data,
UINT swp_flags, const RECT *old_window_rect,
const RECT *old_whole_rect, const RECT *old_client_rect )
{
DWORD style = GetWindowLongW( data->hwnd, GWL_STYLE );
DWORD ex_style = GetWindowLongW( data->hwnd, GWL_EXSTYLE );
XWindowChanges changes;
unsigned int mask = 0;
if (data->managed && data->iconic) return;
/* resizing a managed maximized window is not allowed */
if (!(style & WS_MAXIMIZE) || !data->managed)
{
changes.width = data->whole_rect.right - data->whole_rect.left;
changes.height = data->whole_rect.bottom - data->whole_rect.top;
/* if window rect is empty force size to 1x1 */
if (changes.width <= 0 || changes.height <= 0) changes.width = changes.height = 1;
if (changes.width > 65535) changes.width = 65535;
if (changes.height > 65535) changes.height = 65535;
mask |= CWWidth | CWHeight;
}
/* only the size is allowed to change for the desktop window */
if (data->whole_window != root_window)
{
POINT pt = virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top );
changes.x = pt.x;
changes.y = pt.y;
mask |= CWX | CWY;
}
if (!(swp_flags & SWP_NOZORDER) || (swp_flags & SWP_SHOWWINDOW))
{
/* find window that this one must be after */
HWND prev = GetWindow( data->hwnd, GW_HWNDPREV );
while (prev && !(GetWindowLongW( prev, GWL_STYLE ) & WS_VISIBLE))
prev = GetWindow( prev, GW_HWNDPREV );
if (!prev) /* top child */
{
changes.stack_mode = Above;
mask |= CWStackMode;
}
/* should use stack_mode Below but most window managers don't get it right */
/* and Above with a sibling doesn't work so well either, so we ignore it */
}
set_size_hints( data, style );
set_mwm_hints( data, style, ex_style );
update_net_wm_states( data );
data->configure_serial = NextRequest( data->display );
XReconfigureWMWindow( data->display, data->whole_window, data->vis.screen, mask, &changes );
#ifdef HAVE_LIBXSHAPE
if (IsRectEmpty( old_window_rect ) != IsRectEmpty( &data->window_rect ))
sync_window_region( data, (HRGN)1 );
if (data->shaped)
{
int old_x_offset = old_window_rect->left - old_whole_rect->left;
int old_y_offset = old_window_rect->top - old_whole_rect->top;
int new_x_offset = data->window_rect.left - data->whole_rect.left;
int new_y_offset = data->window_rect.top - data->whole_rect.top;
if (old_x_offset != new_x_offset || old_y_offset != new_y_offset)
XShapeOffsetShape( data->display, data->whole_window, ShapeBounding,
new_x_offset - old_x_offset, new_y_offset - old_y_offset );
}
#endif
TRACE( "win %p/%lx pos %d,%d,%dx%d after %lx changes=%x serial=%lu\n",
data->hwnd, data->whole_window, data->whole_rect.left, data->whole_rect.top,
data->whole_rect.right - data->whole_rect.left,
data->whole_rect.bottom - data->whole_rect.top,
changes.sibling, mask, data->configure_serial );
}
/***********************************************************************
* sync_client_position
*
* Synchronize the X client window position with the Windows one
*/
static void sync_client_position( struct x11drv_win_data *data,
const RECT *old_client_rect, const RECT *old_whole_rect )
{
int mask = 0;
XWindowChanges changes;
if (!data->client_window) return;
changes.x = data->client_rect.left - data->whole_rect.left;
changes.y = data->client_rect.top - data->whole_rect.top;
changes.width = min( max( 1, data->client_rect.right - data->client_rect.left ), 65535 );
changes.height = min( max( 1, data->client_rect.bottom - data->client_rect.top ), 65535 );
if (changes.x != old_client_rect->left - old_whole_rect->left) mask |= CWX;
if (changes.y != old_client_rect->top - old_whole_rect->top) mask |= CWY;
if (changes.width != old_client_rect->right - old_client_rect->left) mask |= CWWidth;
if (changes.height != old_client_rect->bottom - old_client_rect->top) mask |= CWHeight;
if (mask)
{
TRACE( "setting client win %lx pos %d,%d,%dx%d changes=%x\n",
data->client_window, changes.x, changes.y, changes.width, changes.height, mask );
XConfigureWindow( data->display, data->client_window, mask, &changes );
}
}
/***********************************************************************
* move_window_bits
*
* Move the window bits when a window is moved.
*/
static void move_window_bits( HWND hwnd, Window window, const RECT *old_rect, const RECT *new_rect,
const RECT *old_client_rect, const RECT *new_client_rect,
const RECT *new_window_rect )
{
RECT src_rect = *old_rect;
RECT dst_rect = *new_rect;
HDC hdc_src, hdc_dst;
INT code;
HRGN rgn;
HWND parent = 0;
if (!window)
{
OffsetRect( &dst_rect, -new_window_rect->left, -new_window_rect->top );
parent = GetAncestor( hwnd, GA_PARENT );
hdc_src = GetDCEx( parent, 0, DCX_CACHE );
hdc_dst = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
}
else
{
OffsetRect( &dst_rect, -new_client_rect->left, -new_client_rect->top );
/* make src rect relative to the old position of the window */
OffsetRect( &src_rect, -old_client_rect->left, -old_client_rect->top );
if (dst_rect.left == src_rect.left && dst_rect.top == src_rect.top) return;
hdc_src = hdc_dst = GetDCEx( hwnd, 0, DCX_CACHE );
}
rgn = CreateRectRgnIndirect( &dst_rect );
SelectClipRgn( hdc_dst, rgn );
DeleteObject( rgn );
/* WS_CLIPCHILDREN doesn't exclude children from the window update
* region, and ExcludeUpdateRgn call may inappropriately clip valid
* child window contents from the copied parent window bits, but we
* still want to avoid copying invalid window bits when possible.
*/
if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_CLIPCHILDREN ))
ExcludeUpdateRgn( hdc_dst, hwnd );
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",
hwnd, 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 );
rgn = 0;
code = X11DRV_END_EXPOSURES;
ExtEscape( hdc_dst, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, sizeof(rgn), (LPSTR)&rgn );
ReleaseDC( hwnd, hdc_dst );
if (hdc_src != hdc_dst) ReleaseDC( parent, hdc_src );
if (rgn)
{
if (!window)
{
/* map region to client rect since we are using DCX_WINDOW */
OffsetRgn( rgn, new_window_rect->left - new_client_rect->left,
new_window_rect->top - new_client_rect->top );
RedrawWindow( hwnd, NULL, rgn,
RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN );
}
else RedrawWindow( hwnd, NULL, rgn, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
DeleteObject( rgn );
}
}
/***********************************************************************
* get_dummy_parent
*
* Create a dummy parent window for child windows that don't have a true X11 parent.
*/
static Window get_dummy_parent(void)
{
static Window dummy_parent;
if (!dummy_parent)
{
XSetWindowAttributes attrib;
attrib.override_redirect = True;
attrib.border_pixel = 0;
attrib.colormap = default_colormap;
dummy_parent = XCreateWindow( gdi_display, root_window, -1, -1, 1, 1, 0, default_visual.depth,
InputOutput, default_visual.visual,
CWColormap | CWBorderPixel | CWOverrideRedirect, &attrib );
XMapWindow( gdi_display, dummy_parent );
}
return dummy_parent;
}
/**********************************************************************
* create_client_window
*/
Window create_client_window( HWND hwnd, const XVisualInfo *visual )
{
Window dummy_parent = get_dummy_parent();
struct x11drv_win_data *data = get_win_data( hwnd );
XSetWindowAttributes attr;
Window ret;
int x, y, cx, cy;
if (!data)
{
/* explicitly create data for HWND_MESSAGE windows since they can be used for OpenGL */
HWND parent = GetAncestor( hwnd, GA_PARENT );
if (parent == GetDesktopWindow() || GetAncestor( parent, GA_PARENT )) return 0;
if (!(data = alloc_win_data( thread_init_display(), hwnd ))) return 0;
GetClientRect( hwnd, &data->client_rect );
data->window_rect = data->whole_rect = data->client_rect;
}
if (data->client_window)
{
XDeleteContext( data->display, data->client_window, winContext );
XReparentWindow( gdi_display, data->client_window, dummy_parent, 0, 0 );
TRACE( "%p reparent xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window );
}
if (data->client_colormap) XFreeColormap( gdi_display, data->client_colormap );
data->client_colormap = XCreateColormap( gdi_display, dummy_parent, visual->visual,
(visual->class == PseudoColor ||
visual->class == GrayScale ||
visual->class == DirectColor) ? AllocAll : AllocNone );
attr.colormap = data->client_colormap;
attr.bit_gravity = NorthWestGravity;
attr.win_gravity = NorthWestGravity;
attr.backing_store = NotUseful;
attr.border_pixel = 0;
x = data->client_rect.left - data->whole_rect.left;
y = data->client_rect.top - data->whole_rect.top;
cx = min( max( 1, data->client_rect.right - data->client_rect.left ), 65535 );
cy = min( max( 1, data->client_rect.bottom - data->client_rect.top ), 65535 );
ret = data->client_window = XCreateWindow( gdi_display,
data->whole_window ? data->whole_window : dummy_parent,
x, y, cx, cy, 0, default_visual.depth, InputOutput,
visual->visual, CWBitGravity | CWWinGravity |
CWBackingStore | CWColormap | CWBorderPixel, &attr );
if (data->client_window)
{
XSaveContext( data->display, data->client_window, winContext, (char *)data->hwnd );
XMapWindow( gdi_display, data->client_window );
XSync( gdi_display, False );
if (data->whole_window) XSelectInput( data->display, data->client_window, ExposureMask );
TRACE( "%p xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window );
}
release_win_data( data );
return ret;
}
/**********************************************************************
* create_whole_window
*
* Create the whole X window for a given window
*/
static void create_whole_window( struct x11drv_win_data *data )
{
int cx, cy, mask;
XSetWindowAttributes attr;
WCHAR text[1024];
COLORREF key;
BYTE alpha;
DWORD layered_flags;
HRGN win_rgn;
POINT pos;
if (!data->managed && is_window_managed( data->hwnd, SWP_NOACTIVATE, &data->window_rect ))
{
TRACE( "making win %p/%lx managed\n", data->hwnd, data->whole_window );
data->managed = TRUE;
}
if ((win_rgn = CreateRectRgn( 0, 0, 0, 0 )) && GetWindowRgn( data->hwnd, win_rgn ) == ERROR)
{
DeleteObject( win_rgn );
win_rgn = 0;
}
data->shaped = (win_rgn != 0);
if (data->vis.visualid != default_visual.visualid)
data->whole_colormap = XCreateColormap( data->display, root_window, data->vis.visual, AllocNone );
mask = get_window_attributes( data, &attr );
if (!(cx = data->whole_rect.right - data->whole_rect.left)) cx = 1;
else if (cx > 65535) cx = 65535;
if (!(cy = data->whole_rect.bottom - data->whole_rect.top)) cy = 1;
else if (cy > 65535) cy = 65535;
pos = virtual_screen_to_root( data->whole_rect.left, data->whole_rect.top );
data->whole_window = XCreateWindow( data->display, root_window, pos.x, pos.y,
cx, cy, 0, data->vis.depth, InputOutput,
data->vis.visual, mask, &attr );
if (!data->whole_window) goto done;
set_initial_wm_hints( data->display, data->whole_window );
set_wm_hints( data );
XSaveContext( data->display, data->whole_window, winContext, (char *)data->hwnd );
SetPropA( data->hwnd, whole_window_prop, (HANDLE)data->whole_window );
/* set the window text */
if (!InternalGetWindowText( data->hwnd, text, ARRAY_SIZE( text ))) text[0] = 0;
sync_window_text( data->display, data->whole_window, text );
/* set the window region */
if (win_rgn || IsRectEmpty( &data->window_rect )) sync_window_region( data, win_rgn );
/* set the window opacity */
if (!GetLayeredWindowAttributes( data->hwnd, &key, &alpha, &layered_flags )) layered_flags = 0;
sync_window_opacity( data->display, data->whole_window, key, alpha, layered_flags );
XFlush( data->display ); /* make sure the window exists before we start painting to it */
sync_window_cursor( data->whole_window );
done:
if (win_rgn) DeleteObject( win_rgn );
}
/**********************************************************************
* destroy_whole_window
*
* Destroy the whole X window for a given window.
*/
static void destroy_whole_window( struct x11drv_win_data *data, BOOL already_destroyed )
{
TRACE( "win %p xwin %lx/%lx\n", data->hwnd, data->whole_window, data->client_window );
if (data->client_window) XDeleteContext( data->display, data->client_window, winContext );
if (!data->whole_window)
{
if (data->embedded)
{
Window xwin = (Window)GetPropA( data->hwnd, foreign_window_prop );
if (xwin)
{
if (!already_destroyed) XSelectInput( data->display, xwin, 0 );
XDeleteContext( data->display, xwin, winContext );
RemovePropA( data->hwnd, foreign_window_prop );
}
return;
}
}
else
{
if (data->client_window && !already_destroyed)
{
XSelectInput( data->display, data->client_window, 0 );
XReparentWindow( data->display, data->client_window, get_dummy_parent(), 0, 0 );
XSync( data->display, False );
}
XDeleteContext( data->display, data->whole_window, winContext );
if (!already_destroyed) XDestroyWindow( data->display, data->whole_window );
}
if (data->whole_colormap) XFreeColormap( data->display, data->whole_colormap );
data->whole_window = data->client_window = 0;
data->whole_colormap = 0;
data->wm_state = WithdrawnState;
data->net_wm_state = 0;
data->mapped = FALSE;
if (data->xic)
{
XUnsetICFocus( data->xic );
XDestroyIC( data->xic );
data->xic = 0;
}
/* Outlook stops processing messages after destroying a dialog, so we need an explicit flush */
XFlush( data->display );
if (data->surface) window_surface_release( data->surface );
data->surface = NULL;
RemovePropA( data->hwnd, whole_window_prop );
}
/**********************************************************************
* set_window_visual
*
* Change the visual by destroying and recreating the X window if needed.
*/
void set_window_visual( struct x11drv_win_data *data, const XVisualInfo *vis, BOOL use_alpha )
{
Window client_window = data->client_window;
Window whole_window = data->whole_window;
if (!data->use_alpha == !use_alpha) return;
if (data->surface) window_surface_release( data->surface );
data->surface = NULL;
data->use_alpha = use_alpha;
if (data->vis.visualid == vis->visualid) return;
data->client_window = 0;
destroy_whole_window( data, client_window != 0 /* don't destroy whole_window until reparented */ );
data->vis = *vis;
create_whole_window( data );
if (!client_window) return;
/* move the client to the new parent */
XReparentWindow( data->display, client_window, data->whole_window,
data->client_rect.left - data->whole_rect.left,
data->client_rect.top - data->whole_rect.top );
data->client_window = client_window;
XDestroyWindow( data->display, whole_window );
}
/*****************************************************************
* SetWindowText (X11DRV.@)
*/
void CDECL X11DRV_SetWindowText( HWND hwnd, LPCWSTR text )
{
Window win;
if ((win = X11DRV_get_whole_window( hwnd )) && win != DefaultRootWindow(gdi_display))
{
Display *display = thread_init_display();
sync_window_text( display, win, text );
}
}
/***********************************************************************
* SetWindowStyle (X11DRV.@)
*
* Update the X state of a window to reflect a style change
*/
void CDECL X11DRV_SetWindowStyle( HWND hwnd, INT offset, STYLESTRUCT *style )
{
struct x11drv_win_data *data;
DWORD changed = style->styleNew ^ style->styleOld;
if (hwnd == GetDesktopWindow()) return;
if (!(data = get_win_data( hwnd ))) return;
if (!data->whole_window) goto done;
if (offset == GWL_STYLE && (changed & WS_DISABLED)) set_wm_hints( data );
if (offset == GWL_EXSTYLE && (changed & WS_EX_LAYERED)) /* changing WS_EX_LAYERED resets attributes */
{
data->layered = FALSE;
set_window_visual( data, &default_visual, FALSE );
sync_window_opacity( data->display, data->whole_window, 0, 0, 0 );
if (data->surface) set_surface_color_key( data->surface, CLR_INVALID );
}
done:
release_win_data( data );
}
/***********************************************************************
* DestroyWindow (X11DRV.@)
*/
void CDECL X11DRV_DestroyWindow( HWND hwnd )
{
struct x11drv_thread_data *thread_data = x11drv_thread_data();
struct x11drv_win_data *data;
if (!(data = get_win_data( hwnd ))) return;
destroy_whole_window( data, FALSE );
if (thread_data->last_focus == hwnd) thread_data->last_focus = 0;
if (thread_data->last_xic_hwnd == hwnd) thread_data->last_xic_hwnd = 0;
if (data->icon_pixmap) XFreePixmap( gdi_display, data->icon_pixmap );
if (data->icon_mask) XFreePixmap( gdi_display, data->icon_mask );
if (data->client_colormap) XFreeColormap( data->display, data->client_colormap );
HeapFree( GetProcessHeap(), 0, data->icon_bits );
XDeleteContext( gdi_display, (XID)hwnd, win_data_context );
release_win_data( data );
HeapFree( GetProcessHeap(), 0, data );
destroy_gl_drawable( hwnd );
wine_vk_surface_destroy( hwnd );
}
/***********************************************************************
* X11DRV_DestroyNotify
*/
BOOL X11DRV_DestroyNotify( HWND hwnd, XEvent *event )
{
struct x11drv_win_data *data;
BOOL embedded;
if (!(data = get_win_data( hwnd ))) return FALSE;
embedded = data->embedded;
if (!embedded) FIXME( "window %p/%lx destroyed from the outside\n", hwnd, data->whole_window );
destroy_whole_window( data, TRUE );
release_win_data( data );
if (embedded) SendMessageW( hwnd, WM_CLOSE, 0, 0 );
return TRUE;
}
/* initialize the desktop window id in the desktop manager process */
BOOL create_desktop_win_data( Window win )
{
struct x11drv_thread_data *thread_data = x11drv_thread_data();
Display *display = thread_data->display;
struct x11drv_win_data *data;
if (!(data = alloc_win_data( display, GetDesktopWindow() ))) return FALSE;
data->whole_window = win;
data->managed = TRUE;
SetPropA( data->hwnd, whole_window_prop, (HANDLE)win );
set_initial_wm_hints( display, win );
release_win_data( data );
if (thread_data->clip_window) XReparentWindow( display, thread_data->clip_window, win, 0, 0 );
return TRUE;
}
/**********************************************************************
* CreateDesktopWindow (X11DRV.@)
*/
BOOL CDECL X11DRV_CreateDesktopWindow( HWND hwnd )
{
unsigned int width, height;
/* retrieve the real size of the desktop */
SERVER_START_REQ( get_window_rectangles )
{
req->handle = wine_server_user_handle( hwnd );
req->relative = COORDS_CLIENT;
wine_server_call( req );
width = reply->window.right;
height = reply->window.bottom;
}
SERVER_END_REQ;
if (!width && !height) /* not initialized yet */
{
RECT rect = get_virtual_screen_rect();
SERVER_START_REQ( set_window_pos )
{
req->handle = wine_server_user_handle( hwnd );
req->previous = 0;
req->swp_flags = SWP_NOZORDER;
req->window.left = rect.left;
req->window.top = rect.top;
req->window.right = rect.right;
req->window.bottom = rect.bottom;
req->client = req->window;
wine_server_call( req );
}
SERVER_END_REQ;
}
else
{
Window win = (Window)GetPropA( hwnd, whole_window_prop );
if (win && win != root_window) X11DRV_init_desktop( win, width, height );
}
return TRUE;
}
static WNDPROC desktop_orig_wndproc;
#define WM_WINE_NOTIFY_ACTIVITY WM_USER
static LRESULT CALLBACK desktop_wndproc_wrapper( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
switch (msg)
{
case WM_WINE_NOTIFY_ACTIVITY:
{
static ULONGLONG last = 0;
ULONGLONG now = GetTickCount64();
/* calling XResetScreenSaver too often can cause performance
* problems, so throttle it */
if (now > last + 5000)
{
XResetScreenSaver( gdi_display );
XFlush( gdi_display );
last = now;
}
break;
}
}
return desktop_orig_wndproc( hwnd, msg, wp, lp );
}
/**********************************************************************
* CreateWindow (X11DRV.@)
*/
BOOL CDECL X11DRV_CreateWindow( HWND hwnd )
{
if (hwnd == GetDesktopWindow())
{
struct x11drv_thread_data *data = x11drv_init_thread_data();
XSetWindowAttributes attr;
desktop_orig_wndproc = (WNDPROC)SetWindowLongPtrW( hwnd, GWLP_WNDPROC,
(LONG_PTR)desktop_wndproc_wrapper );
/* create the cursor clipping window */
attr.override_redirect = TRUE;
attr.event_mask = StructureNotifyMask | FocusChangeMask;
data->clip_window = XCreateWindow( data->display, root_window, 0, 0, 1, 1, 0, 0,
InputOnly, default_visual.visual,
CWOverrideRedirect | CWEventMask, &attr );
XFlush( data->display );
SetPropA( hwnd, clip_window_prop, (HANDLE)data->clip_window );
X11DRV_InitClipboard();
X11DRV_DisplayDevices_RegisterEventHandlers();
}
return TRUE;
}
/***********************************************************************
* get_win_data
*
* Lock and return the X11 data structure associated with a window.
*/
struct x11drv_win_data *get_win_data( HWND hwnd )
{
char *data;
if (!hwnd) return NULL;
EnterCriticalSection( &win_data_section );
if (!XFindContext( gdi_display, (XID)hwnd, win_data_context, &data ))
return (struct x11drv_win_data *)data;
LeaveCriticalSection( &win_data_section );
return NULL;
}
/***********************************************************************
* release_win_data
*
* Release the data returned by get_win_data.
*/
void release_win_data( struct x11drv_win_data *data )
{
if (data) LeaveCriticalSection( &win_data_section );
}
/***********************************************************************
* X11DRV_create_win_data
*
* Create an X11 data window structure for an existing window.
*/
static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *window_rect,
const RECT *client_rect )
{
Display *display;
struct x11drv_win_data *data;
HWND parent;
if (!(parent = GetAncestor( hwnd, GA_PARENT ))) return NULL; /* desktop */
/* don't create win data for HWND_MESSAGE windows */
if (parent != GetDesktopWindow() && !GetAncestor( parent, GA_PARENT )) return NULL;
if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId()) return NULL;
/* Recreate the parent gl_drawable now that we know there are child windows
* that will need clipping support.
*/
sync_gl_drawable( parent, TRUE );
display = thread_init_display();
init_clip_window(); /* make sure the clip window is initialized in this thread */
if (!(data = alloc_win_data( display, hwnd ))) return NULL;
data->whole_rect = data->window_rect = *window_rect;
data->client_rect = *client_rect;
if (parent == GetDesktopWindow())
{
create_whole_window( data );
TRACE( "win %p/%lx window %s whole %s client %s\n",
hwnd, data->whole_window, wine_dbgstr_rect( &data->window_rect ),
wine_dbgstr_rect( &data->whole_rect ), wine_dbgstr_rect( &data->client_rect ));
}
return data;
}
/* window procedure for foreign windows */
static LRESULT WINAPI foreign_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
switch(msg)
{
case WM_WINDOWPOSCHANGED:
update_systray_balloon_position();
break;
case WM_PARENTNOTIFY:
if (LOWORD(wparam) == WM_DESTROY)
{
TRACE( "%p: got parent notify destroy for win %lx\n", hwnd, lparam );
PostMessageW( hwnd, WM_CLOSE, 0, 0 ); /* so that we come back here once the child is gone */
}
return 0;
case WM_CLOSE:
if (GetWindow( hwnd, GW_CHILD )) return 0; /* refuse to die if we still have children */
break;
}
return DefWindowProcW( hwnd, msg, wparam, lparam );
}
/***********************************************************************
* create_foreign_window
*
* Create a foreign window for the specified X window and its ancestors
*/
HWND create_foreign_window( Display *display, Window xwin )
{
static const WCHAR classW[] = {'_','_','w','i','n','e','_','x','1','1','_','f','o','r','e','i','g','n','_','w','i','n','d','o','w',0};
static BOOL class_registered;
struct x11drv_win_data *data;
HWND hwnd, parent;
POINT pos;
Window xparent, xroot;
Window *xchildren;
unsigned int nchildren;
XWindowAttributes attr;
DWORD style = WS_CLIPCHILDREN;
if (!class_registered)
{
WNDCLASSEXW class;
memset( &class, 0, sizeof(class) );
class.cbSize = sizeof(class);
class.lpfnWndProc = foreign_window_proc;
class.lpszClassName = classW;
if (!RegisterClassExW( &class ) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
{
ERR( "Could not register foreign window class\n" );
return FALSE;
}
class_registered = TRUE;
}
if (XFindContext( display, xwin, winContext, (char **)&hwnd )) hwnd = 0;
if (hwnd) return hwnd; /* already created */
XSelectInput( display, xwin, StructureNotifyMask );
if (!XGetWindowAttributes( display, xwin, &attr ) ||
!XQueryTree( display, xwin, &xroot, &xparent, &xchildren, &nchildren ))
{
XSelectInput( display, xwin, 0 );
return 0;
}
XFree( xchildren );
if (xparent == xroot)
{
parent = GetDesktopWindow();
style |= WS_POPUP;
pos = root_to_virtual_screen( attr.x, attr.y );
}
else
{
parent = create_foreign_window( display, xparent );
style |= WS_CHILD;
pos.x = attr.x;
pos.y = attr.y;
}
hwnd = CreateWindowW( classW, NULL, style, pos.x, pos.y, attr.width, attr.height,
parent, 0, 0, NULL );
if (!(data = alloc_win_data( display, hwnd )))
{
DestroyWindow( hwnd );
return 0;
}
SetRect( &data->window_rect, pos.x, pos.y, pos.x + attr.width, pos.y + attr.height );
data->whole_rect = data->client_rect = data->window_rect;
data->whole_window = data->client_window = 0;
data->embedded = TRUE;
data->mapped = TRUE;
SetPropA( hwnd, foreign_window_prop, (HANDLE)xwin );
XSaveContext( display, xwin, winContext, (char *)data->hwnd );
TRACE( "win %lx parent %p style %08x %s -> hwnd %p\n",
xwin, parent, style, wine_dbgstr_rect(&data->window_rect), hwnd );
release_win_data( data );
ShowWindow( hwnd, SW_SHOW );
return hwnd;
}
/***********************************************************************
* X11DRV_get_whole_window
*
* Return the X window associated with the full area of a window
*/
Window X11DRV_get_whole_window( HWND hwnd )
{
struct x11drv_win_data *data = get_win_data( hwnd );
Window ret;
if (!data)
{
if (hwnd == GetDesktopWindow()) return root_window;
return (Window)GetPropA( hwnd, whole_window_prop );
}
ret = data->whole_window;
release_win_data( data );
return ret;
}
/***********************************************************************
* X11DRV_get_ic
*
* Return the X input context associated with a window
*/
XIC X11DRV_get_ic( HWND hwnd )
{
struct x11drv_win_data *data = get_win_data( hwnd );
XIM xim;
XIC ret = 0;
if (data)
{
x11drv_thread_data()->last_xic_hwnd = hwnd;
ret = data->xic;
if (!ret && (xim = x11drv_thread_data()->xim)) ret = X11DRV_CreateIC( xim, data );
release_win_data( data );
}
return ret;
}
/***********************************************************************
* X11DRV_GetDC (X11DRV.@)
*/
void CDECL X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect,
const RECT *top_rect, DWORD flags )
{
struct x11drv_escape_set_drawable escape;
HWND parent;
escape.code = X11DRV_SET_DRAWABLE;
escape.mode = IncludeInferiors;
escape.dc_rect.left = win_rect->left - top_rect->left;
escape.dc_rect.top = win_rect->top - top_rect->top;
escape.dc_rect.right = win_rect->right - top_rect->left;
escape.dc_rect.bottom = win_rect->bottom - top_rect->top;
if (top == hwnd)
{
struct x11drv_win_data *data = get_win_data( hwnd );
escape.drawable = data ? data->whole_window : X11DRV_get_whole_window( hwnd );
/* special case: when repainting the root window, clip out top-level windows */
if (data && data->whole_window == root_window) escape.mode = ClipByChildren;
release_win_data( data );
}
else
{
/* find the first ancestor that has a drawable */
for (parent = hwnd; parent && parent != top; parent = GetAncestor( parent, GA_PARENT ))
if ((escape.drawable = X11DRV_get_whole_window( parent ))) break;
if (escape.drawable)
{
POINT pt = { 0, 0 };
MapWindowPoints( 0, parent, &pt, 1 );
escape.dc_rect = *win_rect;
OffsetRect( &escape.dc_rect, pt.x, pt.y );
if (flags & DCX_CLIPCHILDREN) escape.mode = ClipByChildren;
}
else escape.drawable = X11DRV_get_whole_window( top );
}
ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
}
/***********************************************************************
* X11DRV_ReleaseDC (X11DRV.@)
*/
void CDECL X11DRV_ReleaseDC( HWND hwnd, HDC hdc )
{
struct x11drv_escape_set_drawable escape;
escape.code = X11DRV_SET_DRAWABLE;
escape.drawable = root_window;
escape.mode = IncludeInferiors;
escape.dc_rect = get_virtual_screen_rect();
OffsetRect( &escape.dc_rect, -2 * escape.dc_rect.left, -2 * escape.dc_rect.top );
ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
}
/*************************************************************************
* ScrollDC (X11DRV.@)
*/
BOOL CDECL X11DRV_ScrollDC( HDC hdc, INT dx, INT dy, HRGN update )
{
RECT rect;
BOOL ret;
HRGN expose_rgn = 0;
GetClipBox( hdc, &rect );
if (update)
{
INT code = X11DRV_START_EXPOSURES;
ExtEscape( hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code, 0, NULL );
ret = BitBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
hdc, rect.left - dx, rect.top - dy, SRCCOPY );
code = X11DRV_END_EXPOSURES;
ExtEscape( hdc, X11DRV_ESCAPE, sizeof(code), (LPSTR)&code,
sizeof(expose_rgn), (LPSTR)&expose_rgn );
if (expose_rgn)
{
CombineRgn( update, update, expose_rgn, RGN_OR );
DeleteObject( expose_rgn );
}
}
else ret = BitBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
hdc, rect.left - dx, rect.top - dy, SRCCOPY );
return ret;
}
/***********************************************************************
* SetCapture (X11DRV.@)
*/
void CDECL X11DRV_SetCapture( HWND hwnd, UINT flags )
{
struct x11drv_thread_data *thread_data = x11drv_thread_data();
struct x11drv_win_data *data;
if (!(flags & (GUI_INMOVESIZE | GUI_INMENUMODE))) return;
if (hwnd)
{
if (!(data = get_win_data( GetAncestor( hwnd, GA_ROOT )))) return;
if (data->whole_window)
{
XFlush( gdi_display );
XGrabPointer( data->display, data->whole_window, False,
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, None, None, CurrentTime );
thread_data->grab_hwnd = data->hwnd;
}
release_win_data( data );
}
else /* release capture */
{
if (!(data = get_win_data( thread_data->grab_hwnd ))) return;
XFlush( gdi_display );
XUngrabPointer( data->display, CurrentTime );
XFlush( data->display );
thread_data->grab_hwnd = NULL;
release_win_data( data );
}
}
/*****************************************************************
* SetParent (X11DRV.@)
*/
void CDECL X11DRV_SetParent( HWND hwnd, HWND parent, HWND old_parent )
{
struct x11drv_win_data *data;
if (parent == old_parent) return;
if (!(data = get_win_data( hwnd ))) return;
if (data->embedded) goto done;
if (parent != GetDesktopWindow()) /* a child window */
{
if (old_parent == GetDesktopWindow())
{
/* destroy the old X windows */
destroy_whole_window( data, FALSE );
data->managed = FALSE;
}
}
else /* new top level window */
{
create_whole_window( data );
}
done:
release_win_data( data );
set_gl_drawable_parent( hwnd, parent );
/* Recreate the parent gl_drawable now that we know there are child windows
* that will need clipping support.
*/
sync_gl_drawable( parent, TRUE );
fetch_icon_data( hwnd, 0, 0 );
}
static inline BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rect )
{
*surface_rect = get_virtual_screen_rect();
if (!IntersectRect( surface_rect, surface_rect, visible_rect )) return FALSE;
OffsetRect( surface_rect, -visible_rect->left, -visible_rect->top );
surface_rect->left &= ~31;
surface_rect->top &= ~31;
surface_rect->right = max( surface_rect->left + 32, (surface_rect->right + 31) & ~31 );
surface_rect->bottom = max( surface_rect->top + 32, (surface_rect->bottom + 31) & ~31 );
return TRUE;
}
/***********************************************************************
* WindowPosChanging (X11DRV.@)
*/
void CDECL X11DRV_WindowPosChanging( HWND hwnd, HWND insert_after, UINT swp_flags,
const RECT *window_rect, const RECT *client_rect, RECT *visible_rect,
struct window_surface **surface )
{
struct x11drv_win_data *data = get_win_data( hwnd );
RECT surface_rect;
DWORD flags;
COLORREF key;
BOOL layered = GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED;
if (!data && !(data = X11DRV_create_win_data( hwnd, window_rect, client_rect ))) return;
/* check if we need to switch the window to managed */
if (!data->managed && data->whole_window && is_window_managed( hwnd, swp_flags, window_rect ))
{
TRACE( "making win %p/%lx managed\n", hwnd, data->whole_window );
release_win_data( data );
unmap_window( hwnd );
if (!(data = get_win_data( hwnd ))) return;
data->managed = TRUE;
}
*visible_rect = *window_rect;
X11DRV_window_to_X_rect( data, visible_rect, window_rect, client_rect );
/* create the window surface if necessary */
if (!data->whole_window && !data->embedded) goto done;
if (swp_flags & SWP_HIDEWINDOW) goto done;
if (data->use_alpha) goto done;
if (!get_surface_rect( visible_rect, &surface_rect )) goto done;
if (*surface) window_surface_release( *surface );
*surface = NULL; /* indicate that we want to draw directly to the window */
if (data->embedded) goto done;
if (data->whole_window == root_window) goto done;
if (data->client_window) goto done;
if (!client_side_graphics && !layered) goto done;
if (data->surface)
{
if (EqualRect( &data->surface->rect, &surface_rect ))
{
/* existing surface is good enough */
window_surface_add_ref( data->surface );
*surface = data->surface;
goto done;
}
}
else if (!(swp_flags & SWP_SHOWWINDOW) && !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE)) goto done;
if (!layered || !GetLayeredWindowAttributes( hwnd, &key, NULL, &flags ) || !(flags & LWA_COLORKEY))
key = CLR_INVALID;
*surface = create_surface( data->whole_window, &data->vis, &surface_rect, key, FALSE );
done:
release_win_data( data );
}
/***********************************************************************
* WindowPosChanged (X11DRV.@)
*/
void CDECL X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, UINT swp_flags,
const RECT *rectWindow, const RECT *rectClient,
const RECT *visible_rect, const RECT *valid_rects,
struct window_surface *surface )
{
struct x11drv_thread_data *thread_data;
struct x11drv_win_data *data;
DWORD new_style = GetWindowLongW( hwnd, GWL_STYLE );
RECT old_window_rect, old_whole_rect, old_client_rect;
int event_type;
if (!(data = get_win_data( hwnd ))) return;
thread_data = x11drv_thread_data();
old_window_rect = data->window_rect;
old_whole_rect = data->whole_rect;
old_client_rect = data->client_rect;
data->window_rect = *rectWindow;
data->whole_rect = *visible_rect;
data->client_rect = *rectClient;
if (data->vis.visualid == default_visual.visualid)
{
if (surface) window_surface_add_ref( surface );
if (data->surface) window_surface_release( data->surface );
data->surface = surface;
}
TRACE( "win %p window %s client %s style %08x flags %08x\n",
hwnd, wine_dbgstr_rect(rectWindow), wine_dbgstr_rect(rectClient), new_style, swp_flags );
if (!IsRectEmpty( &valid_rects[0] ))
{
Window window = data->whole_window;
int x_offset = old_whole_rect.left - data->whole_rect.left;
int y_offset = old_whole_rect.top - data->whole_rect.top;
/* if all that happened is that the whole window moved, copy everything */
if (!(swp_flags & SWP_FRAMECHANGED) &&
old_whole_rect.right - data->whole_rect.right == x_offset &&
old_whole_rect.bottom - data->whole_rect.bottom == y_offset &&
old_client_rect.left - data->client_rect.left == x_offset &&
old_client_rect.right - data->client_rect.right == x_offset &&
old_client_rect.top - data->client_rect.top == y_offset &&
old_client_rect.bottom - data->client_rect.bottom == y_offset &&
EqualRect( &valid_rects[0], &data->client_rect ))
{
/* if we have an X window the bits will be moved by the X server */
if (!window && (x_offset != 0 || y_offset != 0))
{
release_win_data( data );
move_window_bits( hwnd, window, &old_whole_rect, visible_rect,
&old_client_rect, rectClient, rectWindow );
if (!(data = get_win_data( hwnd ))) return;
}
}
else
{
release_win_data( data );
move_window_bits( hwnd, window, &valid_rects[1], &valid_rects[0],
&old_client_rect, rectClient, rectWindow );
if (!(data = get_win_data( hwnd ))) return;
}
}
XFlush( gdi_display ); /* make sure painting is done before we move the window */
sync_client_position( data, &old_client_rect, &old_whole_rect );
if (!data->whole_window)
{
BOOL needs_resize = (!data->client_window &&
(data->client_rect.right - data->client_rect.left !=
old_client_rect.right - old_client_rect.left ||
data->client_rect.bottom - data->client_rect.top !=
old_client_rect.bottom - old_client_rect.top));
release_win_data( data );
if (needs_resize) sync_gl_drawable( hwnd, FALSE );
return;
}
/* check if we are currently processing an event relevant to this window */
event_type = 0;
if (thread_data &&
thread_data->current_event &&
thread_data->current_event->xany.window == data->whole_window)
{
event_type = thread_data->current_event->type;
if (event_type != ConfigureNotify && event_type != PropertyNotify &&
event_type != GravityNotify && event_type != ReparentNotify)
event_type = 0; /* ignore other events */
}
if (data->mapped && event_type != ReparentNotify)
{
if (((swp_flags & SWP_HIDEWINDOW) && !(new_style & WS_VISIBLE)) ||
(!event_type && !(new_style & WS_MINIMIZE) &&
!is_window_rect_mapped( rectWindow ) && is_window_rect_mapped( &old_window_rect )))
{
release_win_data( data );
unmap_window( hwnd );
if (is_window_rect_full_screen( &old_window_rect )) reset_clipping_window();
if (!(data = get_win_data( hwnd ))) return;
}
}
/* don't change position if we are about to minimize or maximize a managed window */
if (!event_type &&
!(data->managed && (swp_flags & SWP_STATECHANGED) && (new_style & (WS_MINIMIZE|WS_MAXIMIZE))))
sync_window_position( data, swp_flags, &old_window_rect, &old_whole_rect, &old_client_rect );
if ((new_style & WS_VISIBLE) &&
((new_style & WS_MINIMIZE) || is_window_rect_mapped( rectWindow )))
{
if (!data->mapped)
{
BOOL needs_icon = !data->icon_pixmap;
BOOL needs_map = TRUE;
/* layered windows are mapped only once their attributes are set */
if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED) needs_map = data->layered;
release_win_data( data );
if (needs_icon) fetch_icon_data( hwnd, 0, 0 );
if (needs_map) map_window( hwnd, new_style );
return;
}
else if ((swp_flags & SWP_STATECHANGED) && (!data->iconic != !(new_style & WS_MINIMIZE)))
{
set_wm_hints( data );
data->iconic = (new_style & WS_MINIMIZE) != 0;
TRACE( "changing win %p iconic state to %u\n", data->hwnd, data->iconic );
if (data->iconic)
XIconifyWindow( data->display, data->whole_window, data->vis.screen );
else if (is_window_rect_mapped( rectWindow ))
XMapWindow( data->display, data->whole_window );
update_net_wm_states( data );
}
else
{
if (swp_flags & (SWP_FRAMECHANGED|SWP_STATECHANGED)) set_wm_hints( data );
if (!event_type) update_net_wm_states( data );
}
}
XFlush( data->display ); /* make sure changes are done before we start painting again */
if (data->surface && data->vis.visualid != default_visual.visualid)
data->surface->funcs->flush( data->surface );
release_win_data( data );
}
/* check if the window icon should be hidden (i.e. moved off-screen) */
static BOOL hide_icon( struct x11drv_win_data *data )
{
static const WCHAR trayW[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
if (data->managed) return TRUE;
/* hide icons in desktop mode when the taskbar is active */
if (!is_virtual_desktop()) return FALSE;
return IsWindowVisible( FindWindowW( trayW, NULL ));
}
/***********************************************************************
* ShowWindow (X11DRV.@)
*/
UINT CDECL X11DRV_ShowWindow( HWND hwnd, INT cmd, RECT *rect, UINT swp )
{
int x, y;
unsigned int width, height, border, depth;
Window root, top;
POINT pos;
DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
struct x11drv_thread_data *thread_data = x11drv_thread_data();
struct x11drv_win_data *data = get_win_data( hwnd );
if (!data || !data->whole_window) goto done;
if (IsRectEmpty( rect )) goto done;
if (style & WS_MINIMIZE)
{
if (((rect->left != -32000 || rect->top != -32000)) && hide_icon( data ))
{
OffsetRect( rect, -32000 - rect->left, -32000 - rect->top );
swp &= ~(SWP_NOMOVE | SWP_NOCLIENTMOVE);
}
goto done;
}
if (!data->managed || !data->mapped || data->iconic) goto done;
/* only fetch the new rectangle if the ShowWindow was a result of a window manager event */
if (!thread_data->current_event || thread_data->current_event->xany.window != data->whole_window)
goto done;
if (thread_data->current_event->type != ConfigureNotify &&
thread_data->current_event->type != PropertyNotify)
goto done;
TRACE( "win %p/%lx cmd %d at %s flags %08x\n",
hwnd, data->whole_window, cmd, wine_dbgstr_rect(rect), swp );
XGetGeometry( thread_data->display, data->whole_window,
&root, &x, &y, &width, &height, &border, &depth );
XTranslateCoordinates( thread_data->display, data->whole_window, root, 0, 0, &x, &y, &top );
pos = root_to_virtual_screen( x, y );
X11DRV_X_to_window_rect( data, rect, pos.x, pos.y, width, height );
swp &= ~(SWP_NOMOVE | SWP_NOCLIENTMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE);
done:
release_win_data( data );
return swp;
}
/**********************************************************************
* SetWindowIcon (X11DRV.@)
*
* hIcon or hIconSm has changed (or is being initialised for the
* first time). Complete the X11 driver-specific initialisation
* and set the window hints.
*/
void CDECL X11DRV_SetWindowIcon( HWND hwnd, UINT type, HICON icon )
{
struct x11drv_win_data *data;
if (!(data = get_win_data( hwnd ))) return;
if (!data->whole_window) goto done;
release_win_data( data ); /* release the lock, fetching the icon requires sending messages */
if (type == ICON_BIG) fetch_icon_data( hwnd, icon, 0 );
else fetch_icon_data( hwnd, 0, icon );
if (!(data = get_win_data( hwnd ))) return;
set_wm_hints( data );
done:
release_win_data( data );
}
/***********************************************************************
* SetWindowRgn (X11DRV.@)
*
* Assign specified region to window (for non-rectangular windows)
*/
void CDECL X11DRV_SetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
{
struct x11drv_win_data *data;
if ((data = get_win_data( hwnd )))
{
sync_window_region( data, hrgn );
release_win_data( data );
}
else if (X11DRV_get_whole_window( hwnd ))
{
SendMessageW( hwnd, WM_X11DRV_SET_WIN_REGION, 0, 0 );
}
}
/***********************************************************************
* SetLayeredWindowAttributes (X11DRV.@)
*
* Set transparency attributes for a layered window.
*/
void CDECL X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha, DWORD flags )
{
struct x11drv_win_data *data = get_win_data( hwnd );
if (data)
{
set_window_visual( data, &default_visual, FALSE );
if (data->whole_window)
sync_window_opacity( data->display, data->whole_window, key, alpha, flags );
if (data->surface)
set_surface_color_key( data->surface, (flags & LWA_COLORKEY) ? key : CLR_INVALID );
data->layered = TRUE;
if (!data->mapped) /* mapping is delayed until attributes are set */
{
DWORD style = GetWindowLongW( data->hwnd, GWL_STYLE );
if ((style & WS_VISIBLE) &&
((style & WS_MINIMIZE) || is_window_rect_mapped( &data->window_rect )))
{
release_win_data( data );
map_window( hwnd, style );
return;
}
}
release_win_data( data );
}
else
{
Window win = X11DRV_get_whole_window( hwnd );
if (win)
{
sync_window_opacity( gdi_display, win, key, alpha, flags );
if (flags & LWA_COLORKEY)
FIXME( "LWA_COLORKEY not supported on foreign process window %p\n", hwnd );
}
}
}
/*****************************************************************************
* UpdateLayeredWindow (X11DRV.@)
*/
BOOL CDECL X11DRV_UpdateLayeredWindow( HWND hwnd, const UPDATELAYEREDWINDOWINFO *info,
const RECT *window_rect )
{
struct window_surface *surface;
struct x11drv_win_data *data;
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, 0 };
COLORREF color_key = (info->dwFlags & ULW_COLORKEY) ? info->crKey : CLR_INVALID;
char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
BITMAPINFO *bmi = (BITMAPINFO *)buffer;
void *src_bits, *dst_bits;
RECT rect, src_rect;
HDC hdc = 0;
HBITMAP dib;
BOOL ret = FALSE;
if (!(data = get_win_data( hwnd ))) return FALSE;
data->layered = TRUE;
if (!data->embedded && argb_visual.visualid) set_window_visual( data, &argb_visual, TRUE );
rect = *window_rect;
OffsetRect( &rect, -window_rect->left, -window_rect->top );
surface = data->surface;
if (!surface || !EqualRect( &surface->rect, &rect ))
{
data->surface = create_surface( data->whole_window, &data->vis, &rect,
color_key, data->use_alpha );
if (surface) window_surface_release( surface );
surface = data->surface;
}
else set_surface_color_key( surface, color_key );
if (surface) window_surface_add_ref( surface );
release_win_data( data );
if (!surface) return FALSE;
if (!info->hdcSrc)
{
window_surface_release( surface );
return TRUE;
}
dst_bits = surface->funcs->get_info( surface, bmi );
if (!(dib = CreateDIBSection( info->hdcDst, bmi, DIB_RGB_COLORS, &src_bits, NULL, 0 ))) goto done;
if (!(hdc = CreateCompatibleDC( 0 ))) goto done;
SelectObject( hdc, dib );
surface->funcs->lock( surface );
if (info->prcDirty)
{
IntersectRect( &rect, &rect, info->prcDirty );
memcpy( src_bits, dst_bits, bmi->bmiHeader.biSizeImage );
PatBlt( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, BLACKNESS );
}
src_rect = rect;
if (info->pptSrc) OffsetRect( &src_rect, info->pptSrc->x, info->pptSrc->y );
DPtoLP( info->hdcSrc, (POINT *)&src_rect, 2 );
ret = GdiAlphaBlend( hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
info->hdcSrc, src_rect.left, src_rect.top,
src_rect.right - src_rect.left, src_rect.bottom - src_rect.top,
(info->dwFlags & ULW_ALPHA) ? *info->pblend : blend );
if (ret)
{
memcpy( dst_bits, src_bits, bmi->bmiHeader.biSizeImage );
add_bounds_rect( surface->funcs->get_bounds( surface ), &rect );
}
surface->funcs->unlock( surface );
surface->funcs->flush( surface );
done:
window_surface_release( surface );
if (hdc) DeleteDC( hdc );
if (dib) DeleteObject( dib );
return ret;
}
/**********************************************************************
* X11DRV_WindowMessage (X11DRV.@)
*/
LRESULT CDECL X11DRV_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
{
struct x11drv_win_data *data;
switch(msg)
{
case WM_X11DRV_UPDATE_CLIPBOARD:
return update_clipboard( hwnd );
case WM_X11DRV_SET_WIN_REGION:
if ((data = get_win_data( hwnd )))
{
sync_window_region( data, (HRGN)1 );
release_win_data( data );
}
return 0;
case WM_X11DRV_RESIZE_DESKTOP:
X11DRV_resize_desktop( (BOOL)lp );
return 0;
case WM_X11DRV_SET_CURSOR:
if ((data = get_win_data( hwnd )))
{
Window win = data->whole_window;
release_win_data( data );
if (win) set_window_cursor( win, (HCURSOR)lp );
}
else if (hwnd == x11drv_thread_data()->clip_hwnd)
set_window_cursor( x11drv_thread_data()->clip_window, (HCURSOR)lp );
return 0;
case WM_X11DRV_CLIP_CURSOR_NOTIFY:
return clip_cursor_notify( hwnd, (HWND)wp, (HWND)lp );
case WM_X11DRV_CLIP_CURSOR_REQUEST:
return clip_cursor_request( hwnd, (BOOL)wp, (BOOL)lp );
default:
FIXME( "got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, wp, lp );
return 0;
}
}
/***********************************************************************
* is_netwm_supported
*/
static BOOL is_netwm_supported( Display *display, Atom atom )
{
static Atom *net_supported;
static int net_supported_count = -1;
int i;
if (net_supported_count == -1)
{
Atom type;
int format;
unsigned long count, remaining;
if (!XGetWindowProperty( display, DefaultRootWindow(display), x11drv_atom(_NET_SUPPORTED), 0,
~0UL, False, XA_ATOM, &type, &format, &count,
&remaining, (unsigned char **)&net_supported ))
net_supported_count = get_property_size( format, count ) / sizeof(Atom);
else
net_supported_count = 0;
}
for (i = 0; i < net_supported_count; i++)
if (net_supported[i] == atom) return TRUE;
return FALSE;
}
/***********************************************************************
* start_screensaver
*/
static LRESULT start_screensaver(void)
{
if (!is_virtual_desktop())
{
const char *argv[3] = { "xdg-screensaver", "activate", NULL };
int pid = _spawnvp( _P_DETACH, argv[0], argv );
if (pid > 0)
{
TRACE( "started process %d\n", pid );
return 0;
}
}
return -1;
}
/***********************************************************************
* SysCommand (X11DRV.@)
*
* Perform WM_SYSCOMMAND handling.
*/
LRESULT CDECL X11DRV_SysCommand( HWND hwnd, WPARAM wparam, LPARAM lparam )
{
WPARAM hittest = wparam & 0x0f;
int dir;
struct x11drv_win_data *data;
if (!(data = get_win_data( hwnd )))
{
if (wparam == SC_SCREENSAVE && hwnd == GetDesktopWindow()) return start_screensaver();
return -1;
}
if (!data->whole_window || !data->managed || !data->mapped) goto failed;
switch (wparam & 0xfff0)
{
case SC_MOVE:
if (!hittest) dir = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
else dir = _NET_WM_MOVERESIZE_MOVE;
break;
case SC_SIZE:
/* windows without WS_THICKFRAME are not resizable through the window manager */
if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_THICKFRAME)) goto failed;
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;
case 9: dir = _NET_WM_MOVERESIZE_MOVE; break;
default: dir = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; break;
}
break;
case SC_KEYMENU:
/* prevent a simple ALT press+release from activating the system menu,
* as that can get confusing on managed windows */
if ((WCHAR)lparam) goto failed; /* got an explicit char */
if (GetMenu( hwnd )) goto failed; /* window has a real menu */
if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) goto failed; /* no system menu */
TRACE( "ignoring SC_KEYMENU wp %lx lp %lx\n", wparam, lparam );
release_win_data( data );
return 0;
default:
goto failed;
}
if (IsZoomed(hwnd)) goto failed;
if (!is_netwm_supported( data->display, x11drv_atom(_NET_WM_MOVERESIZE) ))
{
TRACE( "_NET_WM_MOVERESIZE not supported\n" );
goto failed;
}
release_win_data( data );
move_resize_window( hwnd, dir );
return 0;
failed:
release_win_data( data );
return -1;
}
void CDECL X11DRV_FlashWindowEx( PFLASHWINFO pfinfo )
{
struct x11drv_win_data *data = get_win_data( pfinfo->hwnd );
XEvent xev;
if (!data)
return;
if (data->mapped)
{
xev.type = ClientMessage;
xev.xclient.window = data->whole_window;
xev.xclient.message_type = x11drv_atom( _NET_WM_STATE );
xev.xclient.serial = 0;
xev.xclient.display = data->display;
xev.xclient.send_event = True;
xev.xclient.format = 32;
xev.xclient.data.l[0] = pfinfo->dwFlags ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xev.xclient.data.l[1] = x11drv_atom( _NET_WM_STATE_DEMANDS_ATTENTION );
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 1;
xev.xclient.data.l[4] = 0;
XSendEvent( data->display, DefaultRootWindow( data->display ), False,
SubstructureNotifyMask, &xev );
}
release_win_data( data );
}