mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-31 11:26:10 +00:00
902884e6db
When updating the foreground window, even if both the old and new active window belong to the same non-current thread, the win32u code currently explicitly deactivates the old window. This will cause the transient deactivation of the foreground thread which can lead to undesirable side-effects (e.g., some apps may minimize when they become inactive). Until this is fixed in Wine core, use an internal driver message to ensure that we call NtUserSetForegroundWindow from the context of the new foreground window thread, to avoid the problematic behavior.
752 lines
25 KiB
C
752 lines
25 KiB
C
/*
|
|
* Wayland window handling
|
|
*
|
|
* Copyright 2020 Alexandros Frantzis for Collabora Ltd
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#if 0
|
|
#pragma makedep unix
|
|
#endif
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
|
|
#include "waylanddrv.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
|
|
|
|
/* private window data */
|
|
struct wayland_win_data
|
|
{
|
|
struct rb_entry entry;
|
|
/* hwnd that this private data belongs to */
|
|
HWND hwnd;
|
|
/* wayland surface (if any) for this window */
|
|
struct wayland_surface *wayland_surface;
|
|
/* wine window_surface backing this window */
|
|
struct window_surface *window_surface;
|
|
/* USER window rectangle relative to win32 parent window client area */
|
|
RECT window_rect;
|
|
/* USER client rectangle relative to win32 parent window client area */
|
|
RECT client_rect;
|
|
BOOL managed;
|
|
};
|
|
|
|
static int wayland_win_data_cmp_rb(const void *key,
|
|
const struct rb_entry *entry)
|
|
{
|
|
HWND key_hwnd = (HWND)key; /* cast to work around const */
|
|
const struct wayland_win_data *entry_win_data =
|
|
RB_ENTRY_VALUE(entry, const struct wayland_win_data, entry);
|
|
|
|
if (key_hwnd < entry_win_data->hwnd) return -1;
|
|
if (key_hwnd > entry_win_data->hwnd) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct rb_tree win_data_rb = { wayland_win_data_cmp_rb };
|
|
|
|
/***********************************************************************
|
|
* wayland_win_data_create
|
|
*
|
|
* Create a data window structure for an existing window.
|
|
*/
|
|
static struct wayland_win_data *wayland_win_data_create(HWND hwnd,
|
|
const RECT *window_rect,
|
|
const RECT *client_rect)
|
|
{
|
|
struct wayland_win_data *data;
|
|
struct rb_entry *rb_entry;
|
|
HWND parent;
|
|
|
|
/* Don't create win data for desktop or HWND_MESSAGE windows. */
|
|
if (!(parent = NtUserGetAncestor(hwnd, GA_PARENT))) return NULL;
|
|
if (parent != NtUserGetDesktopWindow() && !NtUserGetAncestor(parent, GA_PARENT))
|
|
return NULL;
|
|
|
|
if (!(data = calloc(1, sizeof(*data)))) return NULL;
|
|
|
|
data->hwnd = hwnd;
|
|
data->window_rect = *window_rect;
|
|
data->client_rect = *client_rect;
|
|
|
|
pthread_mutex_lock(&win_data_mutex);
|
|
|
|
/* Check that another thread hasn't already created the wayland_win_data. */
|
|
if ((rb_entry = rb_get(&win_data_rb, hwnd)))
|
|
{
|
|
free(data);
|
|
return RB_ENTRY_VALUE(rb_entry, struct wayland_win_data, entry);
|
|
}
|
|
|
|
rb_put(&win_data_rb, hwnd, &data->entry);
|
|
|
|
TRACE("hwnd=%p\n", data->hwnd);
|
|
|
|
return data;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* wayland_win_data_destroy
|
|
*/
|
|
static void wayland_win_data_destroy(struct wayland_win_data *data)
|
|
{
|
|
TRACE("hwnd=%p\n", data->hwnd);
|
|
|
|
rb_remove(&win_data_rb, &data->entry);
|
|
|
|
pthread_mutex_unlock(&win_data_mutex);
|
|
|
|
if (data->window_surface)
|
|
{
|
|
wayland_window_surface_update_wayland_surface(data->window_surface, NULL);
|
|
window_surface_release(data->window_surface);
|
|
}
|
|
if (data->wayland_surface) wayland_surface_destroy(data->wayland_surface);
|
|
free(data);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* wayland_win_data_get
|
|
*
|
|
* Lock and return the data structure associated with a window.
|
|
*/
|
|
static struct wayland_win_data *wayland_win_data_get(HWND hwnd)
|
|
{
|
|
struct rb_entry *rb_entry;
|
|
|
|
pthread_mutex_lock(&win_data_mutex);
|
|
|
|
if ((rb_entry = rb_get(&win_data_rb, hwnd)))
|
|
return RB_ENTRY_VALUE(rb_entry, struct wayland_win_data, entry);
|
|
|
|
pthread_mutex_unlock(&win_data_mutex);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* wayland_win_data_release
|
|
*
|
|
* Release the data returned by wayland_win_data_get.
|
|
*/
|
|
static void wayland_win_data_release(struct wayland_win_data *data)
|
|
{
|
|
assert(data);
|
|
pthread_mutex_unlock(&win_data_mutex);
|
|
}
|
|
|
|
static void wayland_win_data_get_config(struct wayland_win_data *data,
|
|
struct wayland_window_config *conf)
|
|
{
|
|
enum wayland_surface_config_state window_state = 0;
|
|
DWORD style;
|
|
|
|
conf->rect = data->window_rect;
|
|
conf->client_rect = data->client_rect;
|
|
style = NtUserGetWindowLongW(data->hwnd, GWL_STYLE);
|
|
|
|
TRACE("window=%s style=%#lx\n", wine_dbgstr_rect(&conf->rect), (long)style);
|
|
|
|
/* The fullscreen state is implied by the window position and style. */
|
|
if (NtUserIsWindowRectFullScreen(&conf->rect))
|
|
{
|
|
if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
|
|
window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
|
|
else if (!(style & WS_MINIMIZE))
|
|
window_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN;
|
|
}
|
|
else if (style & WS_MAXIMIZE)
|
|
{
|
|
window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
|
|
}
|
|
|
|
conf->state = window_state;
|
|
conf->scale = NtUserGetDpiForWindow(data->hwnd) / 96.0;
|
|
conf->visible = (style & WS_VISIBLE) == WS_VISIBLE;
|
|
conf->managed = data->managed;
|
|
}
|
|
|
|
static void wayland_win_data_update_wayland_surface(struct wayland_win_data *data)
|
|
{
|
|
struct wayland_surface *surface = data->wayland_surface;
|
|
HWND parent = NtUserGetAncestor(data->hwnd, GA_PARENT);
|
|
BOOL visible, xdg_visible;
|
|
RECT clip;
|
|
|
|
TRACE("hwnd=%p\n", data->hwnd);
|
|
|
|
/* We don't want wayland surfaces for child windows. */
|
|
if (parent != NtUserGetDesktopWindow() && parent != 0)
|
|
{
|
|
if (data->window_surface)
|
|
wayland_window_surface_update_wayland_surface(data->window_surface, NULL);
|
|
if (surface) wayland_surface_destroy(surface);
|
|
surface = NULL;
|
|
goto out;
|
|
}
|
|
|
|
/* Otherwise ensure that we have a wayland surface. */
|
|
if (!surface && !(surface = wayland_surface_create(data->hwnd))) return;
|
|
|
|
visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE;
|
|
xdg_visible = surface->xdg_toplevel != NULL;
|
|
|
|
pthread_mutex_lock(&surface->mutex);
|
|
|
|
if (visible != xdg_visible)
|
|
{
|
|
/* If we have a pre-existing surface ensure it has no role. */
|
|
if (data->wayland_surface) wayland_surface_clear_role(surface);
|
|
/* If the window is a visible toplevel make it a wayland
|
|
* xdg_toplevel. Otherwise keep it role-less to avoid polluting the
|
|
* compositor with empty xdg_toplevels. */
|
|
if (visible) wayland_surface_make_toplevel(surface);
|
|
}
|
|
|
|
wayland_win_data_get_config(data, &surface->window);
|
|
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
|
|
if (data->window_surface)
|
|
wayland_window_surface_update_wayland_surface(data->window_surface, surface);
|
|
|
|
/* Size/position changes affect the effective pointer constraint, so update
|
|
* it as needed. */
|
|
if (data->hwnd == NtUserGetForegroundWindow() && NtUserGetClipCursor(&clip))
|
|
NtUserClipCursor(&clip);
|
|
|
|
out:
|
|
TRACE("hwnd=%p surface=%p=>%p\n", data->hwnd, data->wayland_surface, surface);
|
|
data->wayland_surface = surface;
|
|
}
|
|
|
|
static void wayland_win_data_update_wayland_state(struct wayland_win_data *data)
|
|
{
|
|
struct wayland_surface *surface = data->wayland_surface;
|
|
BOOL processing_config;
|
|
|
|
pthread_mutex_lock(&surface->mutex);
|
|
|
|
if (!surface->xdg_toplevel) goto out;
|
|
|
|
processing_config = surface->processing.serial &&
|
|
!surface->processing.processed;
|
|
|
|
TRACE("hwnd=%p window_state=%#x %s->state=%#x\n",
|
|
data->hwnd, surface->window.state,
|
|
processing_config ? "processing" : "current",
|
|
processing_config ? surface->processing.state : surface->current.state);
|
|
|
|
/* If we are not processing a compositor requested config, use the
|
|
* window state to determine and update the Wayland state. */
|
|
if (!processing_config)
|
|
{
|
|
/* First do all state unsettings, before setting new state. Some
|
|
* Wayland compositors misbehave if the order is reversed. */
|
|
if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) &&
|
|
(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED))
|
|
{
|
|
xdg_toplevel_unset_maximized(surface->xdg_toplevel);
|
|
}
|
|
if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
|
|
(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
|
|
{
|
|
xdg_toplevel_unset_fullscreen(surface->xdg_toplevel);
|
|
}
|
|
|
|
if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) &&
|
|
!(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED))
|
|
{
|
|
xdg_toplevel_set_maximized(surface->xdg_toplevel);
|
|
}
|
|
if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
|
|
!(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
|
|
{
|
|
xdg_toplevel_set_fullscreen(surface->xdg_toplevel, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
surface->processing.processed = TRUE;
|
|
}
|
|
|
|
out:
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
wl_display_flush(process_wayland.wl_display);
|
|
}
|
|
|
|
static BOOL is_managed(HWND hwnd)
|
|
{
|
|
struct wayland_win_data *data = wayland_win_data_get(hwnd);
|
|
BOOL ret = data && data->managed;
|
|
if (data) wayland_win_data_release(data);
|
|
return ret;
|
|
}
|
|
|
|
static HWND *build_hwnd_list(void)
|
|
{
|
|
NTSTATUS status;
|
|
HWND *list;
|
|
ULONG count = 128;
|
|
|
|
for (;;)
|
|
{
|
|
if (!(list = malloc(count * sizeof(*list)))) return NULL;
|
|
status = NtUserBuildHwndList(0, 0, 0, 0, 0, count, list, &count);
|
|
if (!status) return list;
|
|
free(list);
|
|
if (status != STATUS_BUFFER_TOO_SMALL) return NULL;
|
|
}
|
|
}
|
|
|
|
static BOOL has_owned_popups(HWND hwnd)
|
|
{
|
|
HWND *list;
|
|
UINT i;
|
|
BOOL ret = FALSE;
|
|
|
|
if (!(list = build_hwnd_list())) return FALSE;
|
|
|
|
for (i = 0; list[i] != HWND_BOTTOM; i++)
|
|
{
|
|
if (list[i] == hwnd) break; /* popups are always above owner */
|
|
if (NtUserGetWindowRelative(list[i], GW_OWNER) != hwnd) continue;
|
|
if ((ret = is_managed(list[i]))) break;
|
|
}
|
|
|
|
free(list);
|
|
return ret;
|
|
}
|
|
|
|
static inline HWND get_active_window(void)
|
|
{
|
|
GUITHREADINFO info;
|
|
info.cbSize = sizeof(info);
|
|
return NtUserGetGUIThreadInfo(GetCurrentThreadId(), &info) ? info.hwndActive : 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* 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;
|
|
|
|
/* child windows are not managed */
|
|
style = NtUserGetWindowLongW(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 == get_active_window()) 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 = NtUserMonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
|
|
mi.cbSize = sizeof(mi);
|
|
NtUserGetMonitorInfo(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 = NtUserGetWindowLongW(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;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WAYLAND_DestroyWindow
|
|
*/
|
|
void WAYLAND_DestroyWindow(HWND hwnd)
|
|
{
|
|
struct wayland_win_data *data;
|
|
|
|
TRACE("%p\n", hwnd);
|
|
|
|
if (!(data = wayland_win_data_get(hwnd))) return;
|
|
wayland_win_data_destroy(data);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WAYLAND_WindowPosChanging
|
|
*/
|
|
BOOL WAYLAND_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 wayland_win_data *data = wayland_win_data_get(hwnd);
|
|
HWND parent;
|
|
BOOL visible;
|
|
RECT surface_rect;
|
|
|
|
TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
|
|
hwnd, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
|
|
wine_dbgstr_rect(visible_rect), insert_after, swp_flags);
|
|
|
|
if (!data && !(data = wayland_win_data_create(hwnd, window_rect, client_rect)))
|
|
return TRUE;
|
|
|
|
/* Release the dummy surface wine provides for toplevels. */
|
|
if (*surface) window_surface_release(*surface);
|
|
*surface = NULL;
|
|
|
|
parent = NtUserGetAncestor(hwnd, GA_PARENT);
|
|
visible = ((NtUserGetWindowLongW(hwnd, GWL_STYLE) & WS_VISIBLE) ||
|
|
(swp_flags & SWP_SHOWWINDOW)) &&
|
|
!(swp_flags & SWP_HIDEWINDOW);
|
|
|
|
/* Check if we don't want a dedicated window surface. */
|
|
if ((parent && parent != NtUserGetDesktopWindow()) || !visible) goto done;
|
|
|
|
surface_rect = *window_rect;
|
|
OffsetRect(&surface_rect, -surface_rect.left, -surface_rect.top);
|
|
|
|
/* Check if we can reuse our current window surface. */
|
|
if (data->window_surface &&
|
|
EqualRect(&data->window_surface->rect, &surface_rect))
|
|
{
|
|
window_surface_add_ref(data->window_surface);
|
|
*surface = data->window_surface;
|
|
TRACE("reusing surface %p\n", *surface);
|
|
goto done;
|
|
}
|
|
|
|
*surface = wayland_window_surface_create(data->hwnd, &surface_rect);
|
|
|
|
done:
|
|
wayland_win_data_release(data);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WAYLAND_WindowPosChanged
|
|
*/
|
|
void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
|
|
const RECT *window_rect, const RECT *client_rect,
|
|
const RECT *visible_rect, const RECT *valid_rects,
|
|
struct window_surface *surface)
|
|
{
|
|
struct wayland_win_data *data;
|
|
BOOL managed;
|
|
|
|
TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
|
|
hwnd, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
|
|
wine_dbgstr_rect(visible_rect), insert_after, swp_flags);
|
|
|
|
/* Get the managed state with win_data unlocked, as is_window_managed
|
|
* may need to query win_data information about other HWNDs and thus
|
|
* acquire the lock itself internally. */
|
|
managed = is_window_managed(hwnd, swp_flags, window_rect);
|
|
|
|
if (!(data = wayland_win_data_get(hwnd))) return;
|
|
|
|
data->window_rect = *window_rect;
|
|
data->client_rect = *client_rect;
|
|
data->managed = managed;
|
|
|
|
if (surface) window_surface_add_ref(surface);
|
|
if (data->window_surface) window_surface_release(data->window_surface);
|
|
data->window_surface = surface;
|
|
|
|
wayland_win_data_update_wayland_surface(data);
|
|
if (data->wayland_surface) wayland_win_data_update_wayland_state(data);
|
|
|
|
wayland_win_data_release(data);
|
|
}
|
|
|
|
static void wayland_resize_desktop(void)
|
|
{
|
|
RECT virtual_rect = NtUserGetVirtualScreenRect();
|
|
NtUserSetWindowPos(NtUserGetDesktopWindow(), 0,
|
|
virtual_rect.left, virtual_rect.top,
|
|
virtual_rect.right - virtual_rect.left,
|
|
virtual_rect.bottom - virtual_rect.top,
|
|
SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE);
|
|
}
|
|
|
|
static void wayland_configure_window(HWND hwnd)
|
|
{
|
|
struct wayland_surface *surface;
|
|
INT width, height, window_width, window_height;
|
|
INT window_surf_width, window_surf_height;
|
|
UINT flags = 0;
|
|
uint32_t state;
|
|
DWORD style;
|
|
BOOL needs_enter_size_move = FALSE;
|
|
BOOL needs_exit_size_move = FALSE;
|
|
|
|
if (!(surface = wayland_surface_lock_hwnd(hwnd))) return;
|
|
|
|
if (!surface->xdg_toplevel)
|
|
{
|
|
TRACE("missing xdg_toplevel, returning\n");
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
return;
|
|
}
|
|
|
|
if (!surface->requested.serial)
|
|
{
|
|
TRACE("requested configure event already handled, returning\n");
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
return;
|
|
}
|
|
|
|
surface->processing = surface->requested;
|
|
memset(&surface->requested, 0, sizeof(surface->requested));
|
|
|
|
state = surface->processing.state;
|
|
/* Ignore size hints if we don't have a state that requires strict
|
|
* size adherence, in order to avoid spurious resizes. */
|
|
if (state)
|
|
{
|
|
width = surface->processing.width;
|
|
height = surface->processing.height;
|
|
}
|
|
else
|
|
{
|
|
width = height = 0;
|
|
}
|
|
|
|
if ((state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && !surface->resizing)
|
|
{
|
|
surface->resizing = TRUE;
|
|
needs_enter_size_move = TRUE;
|
|
}
|
|
|
|
if (!(state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && surface->resizing)
|
|
{
|
|
surface->resizing = FALSE;
|
|
needs_exit_size_move = TRUE;
|
|
}
|
|
|
|
/* Transitions between normal/max/fullscreen may entail a frame change. */
|
|
if ((state ^ surface->current.state) &
|
|
(WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED |
|
|
WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
|
|
{
|
|
flags |= SWP_FRAMECHANGED;
|
|
}
|
|
|
|
wayland_surface_coords_from_window(surface,
|
|
surface->window.rect.right -
|
|
surface->window.rect.left,
|
|
surface->window.rect.bottom -
|
|
surface->window.rect.top,
|
|
&window_surf_width, &window_surf_height);
|
|
|
|
/* If the window is already fullscreen and its size is compatible with what
|
|
* the compositor is requesting, don't force a resize, since some applications
|
|
* are very insistent on a particular fullscreen size (which may not match
|
|
* the monitor size). */
|
|
if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
|
|
wayland_surface_config_is_compatible(&surface->processing,
|
|
window_surf_width, window_surf_height,
|
|
surface->window.state))
|
|
{
|
|
flags |= SWP_NOSIZE;
|
|
}
|
|
|
|
wayland_surface_coords_to_window(surface, width, height,
|
|
&window_width, &window_height);
|
|
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
|
|
TRACE("processing=%dx%d,%#x\n", width, height, state);
|
|
|
|
if (needs_enter_size_move) send_message(hwnd, WM_ENTERSIZEMOVE, 0, 0);
|
|
if (needs_exit_size_move) send_message(hwnd, WM_EXITSIZEMOVE, 0, 0);
|
|
|
|
flags |= SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE;
|
|
if (window_width == 0 || window_height == 0) flags |= SWP_NOSIZE;
|
|
|
|
style = NtUserGetWindowLongW(hwnd, GWL_STYLE);
|
|
if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE))
|
|
NtUserSetWindowLong(hwnd, GWL_STYLE, style ^ WS_MAXIMIZE, FALSE);
|
|
|
|
/* The Wayland maximized and fullscreen states are very strict about
|
|
* surface size, so don't let the application override it. The tiled state
|
|
* is not as strict, but it indicates a strong size preference, so try to
|
|
* respect it. */
|
|
if (state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED |
|
|
WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN |
|
|
WAYLAND_SURFACE_CONFIG_STATE_TILED))
|
|
{
|
|
flags |= SWP_NOSENDCHANGING;
|
|
}
|
|
|
|
NtUserSetWindowPos(hwnd, 0, 0, 0, window_width, window_height, flags);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* WAYLAND_WindowMessage
|
|
*/
|
|
LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_WAYLAND_INIT_DISPLAY_DEVICES:
|
|
wayland_init_display_devices(TRUE);
|
|
wayland_resize_desktop();
|
|
return 0;
|
|
case WM_WAYLAND_CONFIGURE:
|
|
wayland_configure_window(hwnd);
|
|
return 0;
|
|
case WM_WAYLAND_SET_FOREGROUND:
|
|
NtUserSetForegroundWindow(hwnd);
|
|
return 0;
|
|
default:
|
|
FIXME("got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, (long)wp, lp);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* WAYLAND_DesktopWindowProc
|
|
*/
|
|
LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_DISPLAYCHANGE:
|
|
wayland_resize_desktop();
|
|
break;
|
|
}
|
|
|
|
return NtUserMessageCall(hwnd, msg, wp, lp, 0, NtUserDefWindowProc, FALSE);
|
|
}
|
|
|
|
static enum xdg_toplevel_resize_edge hittest_to_resize_edge(WPARAM hittest)
|
|
{
|
|
switch (hittest)
|
|
{
|
|
case WMSZ_LEFT: return XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
|
case WMSZ_RIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
|
case WMSZ_TOP: return XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
|
case WMSZ_TOPLEFT: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
|
case WMSZ_TOPRIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
|
case WMSZ_BOTTOM: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
|
case WMSZ_BOTTOMLEFT: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
|
case WMSZ_BOTTOMRIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
|
default: return XDG_TOPLEVEL_RESIZE_EDGE_NONE;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* WAYLAND_SysCommand
|
|
*/
|
|
LRESULT WAYLAND_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
LRESULT ret = -1;
|
|
WPARAM command = wparam & 0xfff0;
|
|
uint32_t button_serial;
|
|
struct wl_seat *wl_seat;
|
|
struct wayland_surface *surface;
|
|
|
|
TRACE("cmd=%lx hwnd=%p, %lx, %lx\n",
|
|
(long)command, hwnd, (long)wparam, lparam);
|
|
|
|
pthread_mutex_lock(&process_wayland.pointer.mutex);
|
|
if (process_wayland.pointer.focused_hwnd == hwnd)
|
|
button_serial = process_wayland.pointer.button_serial;
|
|
else
|
|
button_serial = 0;
|
|
pthread_mutex_unlock(&process_wayland.pointer.mutex);
|
|
|
|
if (command == SC_MOVE || command == SC_SIZE)
|
|
{
|
|
if ((surface = wayland_surface_lock_hwnd(hwnd)))
|
|
{
|
|
pthread_mutex_lock(&process_wayland.seat.mutex);
|
|
wl_seat = process_wayland.seat.wl_seat;
|
|
if (wl_seat && surface->xdg_toplevel && button_serial)
|
|
{
|
|
if (command == SC_MOVE)
|
|
{
|
|
xdg_toplevel_move(surface->xdg_toplevel, wl_seat, button_serial);
|
|
}
|
|
else if (command == SC_SIZE)
|
|
{
|
|
xdg_toplevel_resize(surface->xdg_toplevel, wl_seat, button_serial,
|
|
hittest_to_resize_edge(wparam & 0x0f));
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&process_wayland.seat.mutex);
|
|
pthread_mutex_unlock(&surface->mutex);
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
wl_display_flush(process_wayland.wl_display);
|
|
return ret;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* wayland_window_flush
|
|
*
|
|
* Flush the window_surface associated with a HWND.
|
|
*/
|
|
void wayland_window_flush(HWND hwnd)
|
|
{
|
|
struct wayland_win_data *data = wayland_win_data_get(hwnd);
|
|
|
|
if (!data) return;
|
|
|
|
if (data->window_surface)
|
|
data->window_surface->funcs->flush(data->window_surface);
|
|
|
|
wayland_win_data_release(data);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* wayland_surface_lock_hwnd
|
|
*/
|
|
struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd)
|
|
{
|
|
struct wayland_win_data *data = wayland_win_data_get(hwnd);
|
|
struct wayland_surface *surface;
|
|
|
|
if (!data) return NULL;
|
|
|
|
if ((surface = data->wayland_surface)) pthread_mutex_lock(&surface->mutex);
|
|
|
|
wayland_win_data_release(data);
|
|
|
|
return surface;
|
|
}
|