winewayland.drv: Basic Wayland toplevel surface support.

Use the xdg-shell Wayland protocol to create Wayland xdg_surface
xdg_toplevel surfaces for non-child windows.
This commit is contained in:
Alexandros Frantzis 2023-05-04 13:57:50 +03:00 committed by Alexandre Julliard
parent 0066379200
commit 285c47d15d
7 changed files with 1664 additions and 1 deletions

View file

@ -9,6 +9,8 @@ SOURCES = \
version.rc \
wayland.c \
wayland_output.c \
wayland_surface.c \
waylanddrv_main.c \
window.c \
xdg-output-unstable-v1.xml
xdg-output-unstable-v1.xml \
xdg-shell.xml

View file

@ -38,6 +38,21 @@ struct wayland process_wayland =
.output_mutex = PTHREAD_MUTEX_INITIALIZER
};
/**********************************************************************
* xdg_wm_base handling
*/
static void xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *shell,
uint32_t serial)
{
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener =
{
xdg_wm_base_handle_ping
};
/**********************************************************************
* Registry handling
*/
@ -65,6 +80,20 @@ static void registry_handle_global(void *data, struct wl_registry *registry,
wl_list_for_each(output, &process_wayland.output_list, link)
wayland_output_use_xdg_extension(output);
}
else if (strcmp(interface, "wl_compositor") == 0)
{
process_wayland.wl_compositor =
wl_registry_bind(registry, id, &wl_compositor_interface, 4);
}
else if (strcmp(interface, "xdg_wm_base") == 0)
{
/* Bind version 2 so that compositors (e.g., sway) can properly send tiled
* states, instead of falling back to (ab)using the maximized state. */
process_wayland.xdg_wm_base =
wl_registry_bind(registry, id, &xdg_wm_base_interface,
version < 2 ? version : 2);
xdg_wm_base_add_listener(process_wayland.xdg_wm_base, &xdg_wm_base_listener, NULL);
}
}
static void registry_handle_global_remove(void *data, struct wl_registry *registry,
@ -136,6 +165,18 @@ BOOL wayland_process_init(void)
wl_display_roundtrip_queue(process_wayland.wl_display, process_wayland.wl_event_queue);
wl_display_roundtrip_queue(process_wayland.wl_display, process_wayland.wl_event_queue);
/* Check for required protocol globals. */
if (!process_wayland.wl_compositor)
{
ERR("Wayland compositor doesn't support wl_compositor\n");
return FALSE;
}
if (!process_wayland.xdg_wm_base)
{
ERR("Wayland compositor doesn't support xdg_wm_base\n");
return FALSE;
}
wayland_init_display_devices(FALSE);
process_wayland.initialized = TRUE;

View file

@ -0,0 +1,179 @@
/*
* Wayland surfaces
*
* 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 <stdlib.h>
#include "waylanddrv.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface,
uint32_t serial)
{
struct wayland_surface *surface = data;
TRACE("serial=%u\n", serial);
pthread_mutex_lock(&surface->mutex);
/* Handle this event only if wayland_surface is still associated with
* the target xdg_surface. */
if (surface->xdg_surface == xdg_surface)
xdg_surface_ack_configure(xdg_surface, serial);
pthread_mutex_unlock(&surface->mutex);
}
static const struct xdg_surface_listener xdg_surface_listener =
{
xdg_surface_handle_configure
};
/**********************************************************************
* wayland_surface_create
*
* Creates a role-less wayland surface.
*/
struct wayland_surface *wayland_surface_create(void)
{
struct wayland_surface *surface;
surface = calloc(1, sizeof(*surface));
if (!surface)
{
ERR("Failed to allocate space for Wayland surface\n");
goto err;
}
TRACE("surface=%p\n", surface);
pthread_mutex_init(&surface->mutex, NULL);
surface->wl_surface = wl_compositor_create_surface(process_wayland.wl_compositor);
if (!surface->wl_surface)
{
ERR("Failed to create wl_surface Wayland surface\n");
goto err;
}
return surface;
err:
if (surface) wayland_surface_destroy(surface);
return NULL;
}
/**********************************************************************
* wayland_surface_destroy
*
* Destroys a wayland surface.
*/
void wayland_surface_destroy(struct wayland_surface *surface)
{
pthread_mutex_lock(&surface->mutex);
if (surface->xdg_toplevel)
{
xdg_toplevel_destroy(surface->xdg_toplevel);
surface->xdg_toplevel = NULL;
}
if (surface->xdg_surface)
{
xdg_surface_destroy(surface->xdg_surface);
surface->xdg_surface = NULL;
}
if (surface->wl_surface)
{
wl_surface_destroy(surface->wl_surface);
surface->wl_surface = NULL;
}
pthread_mutex_unlock(&surface->mutex);
wl_display_flush(process_wayland.wl_display);
pthread_mutex_destroy(&surface->mutex);
free(surface);
}
/**********************************************************************
* wayland_surface_make_toplevel
*
* Gives the toplevel role to a plain wayland surface.
*/
void wayland_surface_make_toplevel(struct wayland_surface *surface)
{
TRACE("surface=%p\n", surface);
surface->xdg_surface =
xdg_wm_base_get_xdg_surface(process_wayland.xdg_wm_base, surface->wl_surface);
if (!surface->xdg_surface) goto err;
xdg_surface_add_listener(surface->xdg_surface, &xdg_surface_listener, surface);
surface->xdg_toplevel = xdg_surface_get_toplevel(surface->xdg_surface);
if (!surface->xdg_toplevel) goto err;
wl_surface_commit(surface->wl_surface);
wl_display_flush(process_wayland.wl_display);
return;
err:
wayland_surface_clear_role(surface);
ERR("Failed to assign toplevel role to wayland surface\n");
}
/**********************************************************************
* wayland_surface_clear_role
*
* Clears the role related Wayland objects of a Wayland surface, making it a
* plain surface again. We can later assign the same role (but not a
* different one!) to the surface.
*/
void wayland_surface_clear_role(struct wayland_surface *surface)
{
TRACE("surface=%p\n", surface);
if (surface->xdg_toplevel)
{
xdg_toplevel_destroy(surface->xdg_toplevel);
surface->xdg_toplevel = NULL;
}
if (surface->xdg_surface)
{
xdg_surface_destroy(surface->xdg_surface);
surface->xdg_surface = NULL;
}
/* Ensure no buffer is attached, otherwise future role assignments may fail. */
wl_surface_attach(surface->wl_surface, NULL, 0, 0);
wl_surface_commit(surface->wl_surface);
wl_display_flush(process_wayland.wl_display);
}

View file

@ -28,6 +28,7 @@
#include <pthread.h>
#include <wayland-client.h>
#include "xdg-output-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include "windef.h"
#include "winbase.h"
@ -58,6 +59,8 @@ struct wayland
struct wl_event_queue *wl_event_queue;
struct wl_registry *wl_registry;
struct zxdg_output_manager_v1 *zxdg_output_manager_v1;
struct wl_compositor *wl_compositor;
struct xdg_wm_base *xdg_wm_base;
struct wl_list output_list;
/* Protects the output_list and the wayland_output.current states. */
pthread_mutex_t output_mutex;
@ -91,6 +94,14 @@ struct wayland_output
struct wayland_output_state current;
};
struct wayland_surface
{
struct wl_surface *wl_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
pthread_mutex_t mutex;
};
/**********************************************************************
* Wayland initialization
*/
@ -106,6 +117,15 @@ BOOL wayland_output_create(uint32_t id, uint32_t version) DECLSPEC_HIDDEN;
void wayland_output_destroy(struct wayland_output *output) DECLSPEC_HIDDEN;
void wayland_output_use_xdg_extension(struct wayland_output *output) DECLSPEC_HIDDEN;
/**********************************************************************
* Wayland surface
*/
struct wayland_surface *wayland_surface_create(void) DECLSPEC_HIDDEN;
void wayland_surface_destroy(struct wayland_surface *surface) DECLSPEC_HIDDEN;
void wayland_surface_make_toplevel(struct wayland_surface *surface) DECLSPEC_HIDDEN;
void wayland_surface_clear_role(struct wayland_surface *surface) DECLSPEC_HIDDEN;
/**********************************************************************
* USER driver functions
*/
@ -115,6 +135,10 @@ void WAYLAND_DestroyWindow(HWND hwnd) DECLSPEC_HIDDEN;
BOOL WAYLAND_UpdateDisplayDevices(const struct gdi_device_manager *device_manager,
BOOL force, void *param) DECLSPEC_HIDDEN;
LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) DECLSPEC_HIDDEN;
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) DECLSPEC_HIDDEN;
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) DECLSPEC_HIDDEN;

View file

@ -35,6 +35,7 @@ static const struct user_driver_funcs waylanddrv_funcs =
.pDestroyWindow = WAYLAND_DestroyWindow,
.pUpdateDisplayDevices = WAYLAND_UpdateDisplayDevices,
.pWindowMessage = WAYLAND_WindowMessage,
.pWindowPosChanged = WAYLAND_WindowPosChanged,
.pWindowPosChanging = WAYLAND_WindowPosChanging
};

View file

@ -39,6 +39,8 @@ 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;
};
static int wayland_win_data_cmp_rb(const void *key,
@ -103,6 +105,7 @@ static void wayland_win_data_destroy(struct wayland_win_data *data)
pthread_mutex_unlock(&win_data_mutex);
if (data->wayland_surface) wayland_surface_destroy(data->wayland_surface);
free(data);
}
@ -136,6 +139,47 @@ static void wayland_win_data_release(struct wayland_win_data *data)
pthread_mutex_unlock(&win_data_mutex);
}
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;
TRACE("hwnd=%p\n", data->hwnd);
/* We don't want wayland surfaces for child windows. */
if (parent != NtUserGetDesktopWindow() && parent != 0)
{
if (surface) wayland_surface_destroy(surface);
surface = NULL;
goto out;
}
/* Otherwise ensure that we have a wayland surface. */
if (!surface && !(surface = wayland_surface_create())) return;
visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE;
xdg_visible = surface->xdg_toplevel != NULL;
if (visible != xdg_visible)
{
pthread_mutex_lock(&surface->mutex);
/* 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);
pthread_mutex_unlock(&surface->mutex);
}
out:
TRACE("hwnd=%p surface=%p=>%p\n", data->hwnd, data->wayland_surface, surface);
data->wayland_surface = surface;
}
/***********************************************************************
* WAYLAND_DestroyWindow
*/
@ -169,6 +213,27 @@ BOOL WAYLAND_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags,
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 = wayland_win_data_get(hwnd);
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) return;
wayland_win_data_update_wayland_surface(data);
wayland_win_data_release(data);
}
static void wayland_resize_desktop(void)
{
RECT virtual_rect = NtUserGetVirtualScreenRect();

File diff suppressed because it is too large Load diff