GHOST/Wayland: support window activation
Calling render (for example) with an existing window open now activates the window on Wayland. Tested to work on GNOME & KDE. Use the xdg-activation protocol which typically brings the window to the foreground. Partially resolves #102985.
This commit is contained in:
@@ -398,6 +398,10 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||
)
|
||||
# `xdg-activation`.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||
)
|
||||
# Pointer-constraints.
|
||||
generate_protocol_bindings(
|
||||
"${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
#include <primary-selection-unstable-v1-client-protocol.h>
|
||||
#include <relative-pointer-unstable-v1-client-protocol.h>
|
||||
#include <tablet-unstable-v2-client-protocol.h>
|
||||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-output-unstable-v1-client-protocol.h>
|
||||
|
||||
/* Decorations `xdg_decor`. */
|
||||
@@ -934,6 +935,7 @@ struct GWL_Display {
|
||||
struct zwp_tablet_manager_v2 *wp_tablet_manager = nullptr;
|
||||
struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
|
||||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
|
||||
struct xdg_activation_v1 *xdg_activation_manager = nullptr;
|
||||
|
||||
struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
|
||||
@@ -5161,6 +5163,24 @@ static void gwl_registry_wp_pointer_gestures_remove(GWL_Display *display,
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.xdg_activation */
|
||||
|
||||
static void gwl_registry_xdg_activation_add(GWL_Display *display,
|
||||
const GWL_RegisteryAdd_Params *params)
|
||||
{
|
||||
display->xdg_activation_manager = static_cast<xdg_activation_v1 *>(
|
||||
wl_registry_bind(display->wl_registry, params->name, &xdg_activation_v1_interface, 1));
|
||||
gwl_registry_entry_add(display, params, nullptr);
|
||||
}
|
||||
static void gwl_registry_xdg_activation_remove(GWL_Display *display,
|
||||
void * /*user_data*/,
|
||||
const bool /*on_exit*/)
|
||||
{
|
||||
struct xdg_activation_v1 **value_p = &display->xdg_activation_manager;
|
||||
xdg_activation_v1_destroy(*value_p);
|
||||
*value_p = nullptr;
|
||||
}
|
||||
|
||||
/* #GWL_Display.wp_primary_selection_device_manager */
|
||||
|
||||
static void gwl_registry_wp_primary_selection_device_manager_add(
|
||||
@@ -5264,6 +5284,12 @@ static const GWL_RegistryHandler gwl_registry_handlers[] = {
|
||||
nullptr,
|
||||
gwl_registry_wp_pointer_gestures_remove,
|
||||
},
|
||||
{
|
||||
&xdg_activation_v1_interface.name,
|
||||
gwl_registry_xdg_activation_add,
|
||||
nullptr,
|
||||
gwl_registry_xdg_activation_remove,
|
||||
},
|
||||
/* Display outputs. */
|
||||
{
|
||||
&wl_output_interface.name,
|
||||
@@ -6823,6 +6849,11 @@ struct zwp_primary_selection_device_manager_v1 *GHOST_SystemWayland::wp_primary_
|
||||
return display_->wp_primary_selection_device_manager;
|
||||
}
|
||||
|
||||
struct xdg_activation_v1 *GHOST_SystemWayland::xdg_activation_manager()
|
||||
{
|
||||
return display_->xdg_activation_manager;
|
||||
}
|
||||
|
||||
struct zwp_pointer_gestures_v1 *GHOST_SystemWayland::wp_pointer_gestures()
|
||||
{
|
||||
return display_->wp_pointer_gestures;
|
||||
@@ -6934,6 +6965,16 @@ void GHOST_SystemWayland::seat_active_set(const struct GWL_Seat *seat)
|
||||
gwl_display_seat_active_set(display_, seat);
|
||||
}
|
||||
|
||||
struct wl_seat *GHOST_SystemWayland::wl_seat_active_get_with_input_serial(uint32_t &serial)
|
||||
{
|
||||
GWL_Seat *seat = gwl_display_seat_active_get(display_);
|
||||
if (seat) {
|
||||
serial = seat->data_source_serial;
|
||||
return seat->wl_seat;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GHOST_SystemWayland::window_surface_unref(const wl_surface *wl_surface)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
@@ -179,6 +179,7 @@ class GHOST_SystemWayland : public GHOST_System {
|
||||
struct wl_display *wl_display();
|
||||
struct wl_compositor *wl_compositor();
|
||||
struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_manager();
|
||||
struct xdg_activation_v1 *xdg_activation_manager();
|
||||
struct zwp_pointer_gestures_v1 *wp_pointer_gestures();
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
@@ -205,6 +206,8 @@ class GHOST_SystemWayland : public GHOST_System {
|
||||
/** Set this seat to be active. */
|
||||
void seat_active_set(const struct GWL_Seat *seat);
|
||||
|
||||
struct wl_seat *wl_seat_active_get_with_input_serial(uint32_t &serial);
|
||||
|
||||
/**
|
||||
* Clear all references to this output.
|
||||
*
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#endif
|
||||
|
||||
/* Generated by `wayland-scanner`. */
|
||||
#include <xdg-activation-v1-client-protocol.h>
|
||||
#include <xdg-decoration-unstable-v1-client-protocol.h>
|
||||
#include <xdg-shell-client-protocol.h>
|
||||
|
||||
@@ -43,6 +44,8 @@
|
||||
/* Logging, use `ghost.wl.*` prefix. */
|
||||
#include "CLG_log.h"
|
||||
|
||||
static const struct xdg_activation_token_v1_listener *xdg_activation_listener_get();
|
||||
|
||||
static constexpr size_t base_dpi = 96;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
@@ -111,6 +114,9 @@ struct GWL_Window {
|
||||
*/
|
||||
wl_fixed_t scale_fractional = 0;
|
||||
|
||||
/** A temporary token used for the window to be notified of of it's activation. */
|
||||
struct xdg_activation_token_v1 *xdg_activation_token = nullptr;
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
WGL_LibDecor_Window *libdecor = nullptr;
|
||||
#endif
|
||||
@@ -281,6 +287,61 @@ static bool gwl_window_state_set(GWL_Window *win, const GHOST_TWindowState state
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Activation
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Unlike #GHOST_WindowWayland::activate which responds to WAYLAND having set the window active.
|
||||
* This function makes a request to WAYLAND for the window to become active.
|
||||
*
|
||||
* \note The request may be ignored and may not work on all compositors.
|
||||
*/
|
||||
static void gwl_window_activate(GWL_Window *win)
|
||||
{
|
||||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
struct xdg_activation_v1 *activation_manager = system->xdg_activation_manager();
|
||||
if (UNLIKELY(activation_manager == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (win->xdg_activation_token) {
|
||||
/* We're about to overwrite this with a new request. */
|
||||
xdg_activation_token_v1_destroy(win->xdg_activation_token);
|
||||
}
|
||||
win->xdg_activation_token = xdg_activation_v1_get_activation_token(activation_manager);
|
||||
|
||||
xdg_activation_token_v1_add_listener(
|
||||
win->xdg_activation_token, xdg_activation_listener_get(), win);
|
||||
|
||||
xdg_activation_token_v1_set_app_id(win->xdg_activation_token, GHOST_SystemWayland::xdg_app_id());
|
||||
|
||||
/* The serial of the input device requesting activation. */
|
||||
{
|
||||
uint32_t serial = 0;
|
||||
struct wl_seat *seat = system->wl_seat_active_get_with_input_serial(serial);
|
||||
if (seat) {
|
||||
xdg_activation_token_v1_set_serial(win->xdg_activation_token, serial, seat);
|
||||
}
|
||||
}
|
||||
|
||||
/* The surface of the window requesting activation. */
|
||||
{
|
||||
GHOST_WindowWayland *ghost_window_active = static_cast<GHOST_WindowWayland *>(
|
||||
system->getWindowManager()->getActiveWindow());
|
||||
if (ghost_window_active) {
|
||||
struct wl_surface *surface = ghost_window_active->wl_surface();
|
||||
if (surface) {
|
||||
xdg_activation_token_v1_set_surface(win->xdg_activation_token, surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xdg_activation_token_v1_commit(win->xdg_activation_token);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal #GWL_Window Pending Actions
|
||||
* \{ */
|
||||
@@ -521,6 +582,39 @@ static const xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (XDG Activation), #xdg_activation_v1_interface
|
||||
*
|
||||
* Used by #gwl_window_activate.
|
||||
* \{ */
|
||||
|
||||
static void xdg_activation_handle_done(void *data,
|
||||
struct xdg_activation_token_v1 *xdg_activation_token_v1,
|
||||
const char *token)
|
||||
{
|
||||
GWL_Window *win = static_cast<GWL_Window *>(data);
|
||||
if (xdg_activation_token_v1 != win->xdg_activation_token) {
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_SystemWayland *system = win->ghost_system;
|
||||
struct xdg_activation_v1 *activation_manager = system->xdg_activation_manager();
|
||||
xdg_activation_v1_activate(activation_manager, token, win->wl_surface);
|
||||
xdg_activation_token_v1_destroy(win->xdg_activation_token);
|
||||
win->xdg_activation_token = nullptr;
|
||||
}
|
||||
|
||||
static const struct xdg_activation_token_v1_listener xdg_activation_listener = {
|
||||
/*done*/ xdg_activation_handle_done,
|
||||
};
|
||||
|
||||
static const struct xdg_activation_token_v1_listener *xdg_activation_listener_get()
|
||||
{
|
||||
return &xdg_activation_listener;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Listener (LibDecor Frame), #libdecor_frame_interface
|
||||
* \{ */
|
||||
@@ -1060,6 +1154,11 @@ GHOST_WindowWayland::~GHOST_WindowWayland()
|
||||
|
||||
wl_egl_window_destroy(window_->egl_window);
|
||||
|
||||
if (window_->xdg_activation_token) {
|
||||
xdg_activation_token_v1_destroy(window_->xdg_activation_token);
|
||||
window_->xdg_activation_token = nullptr;
|
||||
}
|
||||
|
||||
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
|
||||
if (use_libdecor) {
|
||||
gwl_libdecor_window_destroy(window_->libdecor);
|
||||
@@ -1123,8 +1222,16 @@ GHOST_TSuccess GHOST_WindowWayland::invalidate()
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/)
|
||||
GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder order)
|
||||
{
|
||||
/* NOTE(@ideasman42): only activation is supported (on X11 & Cocoa for e.g.)
|
||||
* both activation and raising is performed. Since WAYLAND only supports activation,
|
||||
* do that as the compositor will likely raise the window as well.
|
||||
* Although it's not ideal that raising does something but lowering a window is ignored. */
|
||||
if (order == GHOST_kWindowOrderTop) {
|
||||
gwl_window_activate(window_);
|
||||
}
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user