wayland: Implement SetWindowOpacity via the alpha modifier protocol

The wp_alpha_modifier_v1 protocol allows for a global blending factor to be specified for an entire surface. Use this to add support for SDL_SetWindowOpacity().
This commit is contained in:
Frank Praznik
2024-05-14 13:10:09 -04:00
parent 606903c02f
commit 716dc0e1bf
5 changed files with 155 additions and 10 deletions

View File

@@ -43,6 +43,7 @@
#include <wayland-util.h>
#include "alpha-modifier-v1-client-protocol.h"
#include "cursor-shape-v1-client-protocol.h"
#include "fractional-scale-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
@@ -487,6 +488,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize;
device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize;
device->SetWindowModalFor = Wayland_SetWindowModalFor;
device->SetWindowOpacity = Wayland_SetWindowOpacity;
device->SetWindowTitle = Wayland_SetWindowTitle;
device->GetWindowSizeInPixels = Wayland_GetWindowSizeInPixels;
device->GetDisplayForWindow = Wayland_GetDisplayForWindow;
@@ -1107,6 +1109,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
d->zxdg_exporter_v2 = wl_registry_bind(d->registry, id, &zxdg_exporter_v2_interface, 1);
} else if (SDL_strcmp(interface, "xdg_wm_dialog_v1") == 0) {
d->xdg_wm_dialog_v1 = wl_registry_bind(d->registry, id, &xdg_wm_dialog_v1_interface, 1);
} else if (SDL_strcmp(interface, "wp_alpha_modifier_v1") == 0) {
d->wp_alpha_modifier_v1 = wl_registry_bind(d->registry, id, &wp_alpha_modifier_v1_interface, 1);
} else if (SDL_strcmp(interface, "kde_output_order_v1") == 0) {
d->kde_output_order = wl_registry_bind(d->registry, id, &kde_output_order_v1_interface, 1);
kde_output_order_v1_add_listener(d->kde_output_order, &kde_output_order_listener, d);
@@ -1369,6 +1373,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
data->xdg_wm_dialog_v1 = NULL;
}
if (data->wp_alpha_modifier_v1) {
wp_alpha_modifier_v1_destroy(data->wp_alpha_modifier_v1);
data->wp_alpha_modifier_v1 = NULL;
}
if (data->kde_output_order) {
Wayland_FlushOutputOrder(data);
kde_output_order_v1_destroy(data->kde_output_order);

View File

@@ -81,6 +81,7 @@ struct SDL_VideoData
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
struct zxdg_exporter_v2 *zxdg_exporter_v2;
struct xdg_wm_dialog_v1 *xdg_wm_dialog_v1;
struct wp_alpha_modifier_v1 *wp_alpha_modifier_v1;
struct kde_output_order_v1 *kde_output_order;
struct xkb_context *xkb_context;

View File

@@ -32,6 +32,7 @@
#include "SDL_waylandvideo.h"
#include "../../SDL_hints_c.h"
#include "alpha-modifier-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
@@ -279,10 +280,24 @@ static void RepositionPopup(SDL_Window *window, SDL_bool use_current_position)
}
}
static void SetSurfaceOpaqueRegion(SDL_WindowData *wind, SDL_bool is_opaque)
{
SDL_VideoData *viddata = wind->waylandData;
if (is_opaque) {
struct wl_region *region = wl_compositor_create_region(viddata->compositor);
wl_region_add(region, 0, 0,
wind->current.logical_width, wind->current.logical_height);
wl_surface_set_opaque_region(wind->surface, region);
wl_region_destroy(region);
} else {
wl_surface_set_opaque_region(wind->surface, NULL);
}
}
static void ConfigureWindowGeometry(SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
SDL_VideoData *viddata = data->waylandData;
const int old_pixel_width = data->current.pixel_width;
const int old_pixel_height = data->current.pixel_height;
int window_width, window_height;
@@ -391,20 +406,12 @@ static void ConfigureWindowGeometry(SDL_Window *window)
* need to be recalculated if the output size has changed.
*/
if (window_size_changed) {
struct wl_region *region;
/* libdecor does this internally on frame commits, so it's only needed for xdg surfaces. */
if (data->shell_surface_type != WAYLAND_SURFACE_LIBDECOR && data->shell_surface.xdg.surface) {
xdg_surface_set_window_geometry(data->shell_surface.xdg.surface, 0, 0, data->current.logical_width, data->current.logical_height);
}
if (!(window->flags & SDL_WINDOW_TRANSPARENT)) {
region = wl_compositor_create_region(viddata->compositor);
wl_region_add(region, 0, 0,
data->current.logical_width, data->current.logical_height);
wl_surface_set_opaque_region(data->surface, region);
wl_region_destroy(region);
}
SetSurfaceOpaqueRegion(data, !(window->flags & SDL_WINDOW_TRANSPARENT) && window->opacity == 1.0f);
/* Ensure that child popup windows are still in bounds. */
for (SDL_Window *child = window->first_child; child; child = child->next_sibling) {
@@ -2302,6 +2309,11 @@ int Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Propert
}
}
if (!custom_surface_role && c->wp_alpha_modifier_v1) {
data->wp_alpha_modifier_surface_v1 = wp_alpha_modifier_v1_get_surface(c->wp_alpha_modifier_v1, data->surface);
wp_alpha_modifier_surface_v1_set_multiplier(data->wp_alpha_modifier_surface_v1, SDL_MAX_UINT32);
}
/* Must be called before EGL configuration to set the drawable backbuffer size. */
ConfigureWindowGeometry(window);
@@ -2494,6 +2506,20 @@ SDL_DisplayID Wayland_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *wi
return 0;
}
int Wayland_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
{
SDL_WindowData *wind = window->driverdata;
if (wind->wp_alpha_modifier_surface_v1) {
SetSurfaceOpaqueRegion(wind, !(window->flags & SDL_WINDOW_TRANSPARENT) && opacity == 1.0f);
wp_alpha_modifier_surface_v1_set_multiplier(wind->wp_alpha_modifier_surface_v1, (Uint32)((double)SDL_MAX_UINT32 * (double)opacity));
return 0;
}
return SDL_SetError("wayland: set window opacity failed; compositor lacks support for the required wp_alpha_modifier_v1 protocol");
}
void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
@@ -2614,6 +2640,10 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
wp_fractional_scale_v1_destroy(wind->fractional_scale);
}
if (wind->wp_alpha_modifier_surface_v1) {
wp_alpha_modifier_surface_v1_destroy(wind->wp_alpha_modifier_surface_v1);
}
SDL_free(wind->outputs);
SDL_free(wind->app_id);

View File

@@ -97,6 +97,7 @@ struct SDL_WindowData
struct wp_fractional_scale_v1 *fractional_scale;
struct zxdg_exported_v2 *exported;
struct xdg_dialog_v1 *xdg_dialog_v1;
struct wp_alpha_modifier_surface_v1 *wp_alpha_modifier_surface_v1;
SDL_AtomicInt swap_interval_ready;
@@ -193,6 +194,7 @@ extern void Wayland_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *win
extern void Wayland_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h);
extern SDL_DisplayID Wayland_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern int Wayland_SetWindowModalFor(SDL_VideoDevice *_this, SDL_Window *modal_window, SDL_Window *parent_window);
extern int Wayland_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern void Wayland_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern void Wayland_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);