mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-11-23 02:43:30 +08:00
wayland: Use the preferred order of displays exposed by KDE
KDE provides the kde_output_order_v1 protocol, which tells clients the preferred order of all connected displays. Sort SDL displays according to the provided list at init time.
This commit is contained in:
parent
e0c2cca629
commit
e71e16950a
@ -57,6 +57,7 @@
|
||||
#include "input-timestamps-unstable-v1-client-protocol.h"
|
||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "kde-output-order-v1-client-protocol.h"
|
||||
|
||||
#ifdef HAVE_LIBDECOR_H
|
||||
#include <libdecor.h>
|
||||
@ -96,7 +97,7 @@
|
||||
/* GNOME doesn't expose displays in any particular order, but we can find the
|
||||
* primary display and its logical coordinates via a DBus method.
|
||||
*/
|
||||
static SDL_bool Wayland_GetPrimaryDisplayCoordinates(int *x, int *y)
|
||||
static SDL_bool Wayland_GetGNOMEPrimaryDisplayCoordinates(int *x, int *y)
|
||||
{
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||
@ -193,6 +194,97 @@ error:
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
static void Wayland_FlushOutputOrder(SDL_VideoData *vid)
|
||||
{
|
||||
SDL_WaylandConnectorName *c, *tmp;
|
||||
wl_list_for_each_safe (c, tmp, &vid->output_order, link) {
|
||||
WAYLAND_wl_list_remove(&c->link);
|
||||
SDL_free(c);
|
||||
}
|
||||
|
||||
vid->output_order_finalized = SDL_FALSE;
|
||||
}
|
||||
|
||||
/* The order of wl_output displays exposed by KDE doesn't correspond to any priority, but KDE does provide a protocol
|
||||
* that tells clients the preferred order or all connected displays via an ordered list of connector name strings.
|
||||
*/
|
||||
static void handle_kde_output_order_output(void *data, struct kde_output_order_v1 *kde_output_order_v1, const char *output_name)
|
||||
{
|
||||
SDL_VideoData *vid = (SDL_VideoData *)data;
|
||||
|
||||
/* Starting a new list, flush the old. */
|
||||
if (vid->output_order_finalized) {
|
||||
Wayland_FlushOutputOrder(vid);
|
||||
}
|
||||
|
||||
const int len = SDL_strlen(output_name) + 1;
|
||||
SDL_WaylandConnectorName *node = SDL_malloc(sizeof(SDL_WaylandConnectorName) + len);
|
||||
SDL_strlcpy(node->wl_output_name, output_name, len);
|
||||
|
||||
WAYLAND_wl_list_insert(vid->output_order.prev, &node->link);
|
||||
}
|
||||
|
||||
static void handle_kde_output_order_done(void *data, struct kde_output_order_v1 *kde_output_order_v1)
|
||||
{
|
||||
SDL_VideoData *vid = (SDL_VideoData *)data;
|
||||
vid->output_order_finalized = SDL_TRUE;
|
||||
}
|
||||
|
||||
static const struct kde_output_order_v1_listener kde_output_order_listener = {
|
||||
handle_kde_output_order_output,
|
||||
handle_kde_output_order_done
|
||||
};
|
||||
|
||||
static void Wayland_SortOutputs(SDL_VideoData *vid)
|
||||
{
|
||||
SDL_DisplayData *d;
|
||||
int p_x, p_y;
|
||||
|
||||
/* KDE provides the kde-output-order-v1 protocol, which gives us the full preferred display
|
||||
* ordering in the form of a list of wl_output.name strings (connector names).
|
||||
*/
|
||||
if (!WAYLAND_wl_list_empty(&vid->output_order)) {
|
||||
struct wl_list sorted_list;
|
||||
SDL_WaylandConnectorName *c;
|
||||
|
||||
/* Sort the outputs by connector name. */
|
||||
WAYLAND_wl_list_init(&sorted_list);
|
||||
wl_list_for_each (c, &vid->output_order, link) {
|
||||
wl_list_for_each (d, &vid->output_list, link) {
|
||||
if (SDL_strcmp(c->wl_output_name, d->wl_output_name) == 0) {
|
||||
/* Remove from the current list and Append the next node to the end of the new list. */
|
||||
WAYLAND_wl_list_remove(&d->link);
|
||||
WAYLAND_wl_list_insert(sorted_list.prev, &d->link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!WAYLAND_wl_list_empty(&vid->output_list)) {
|
||||
/* If any displays were omitted during the sort, append them to the new list.
|
||||
* This shouldn't happen, but better safe than sorry.
|
||||
*/
|
||||
WAYLAND_wl_list_insert_list(sorted_list.prev, &vid->output_list);
|
||||
}
|
||||
|
||||
/* Set the output list to the sorted list. */
|
||||
WAYLAND_wl_list_init(&vid->output_list);
|
||||
WAYLAND_wl_list_insert_list(&vid->output_list, &sorted_list);
|
||||
} else if (Wayland_GetGNOMEPrimaryDisplayCoordinates(&p_x, &p_y)) {
|
||||
/* GNOME doesn't expose the displays in any preferential order, so find the primary display coordinates and use them
|
||||
* to manually sort the primary display to the front of the list so that it is always the first exposed by SDL.
|
||||
* Otherwise, assume that the displays were already exposed in preferential order.
|
||||
*/
|
||||
wl_list_for_each (d, &vid->output_list, link) {
|
||||
if (d->x == p_x && d->y == p_y) {
|
||||
WAYLAND_wl_list_remove(&d->link);
|
||||
WAYLAND_wl_list_insert(&vid->output_list, &d->link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void display_handle_done(void *data, struct wl_output *output);
|
||||
|
||||
/* Initialization/Query functions */
|
||||
@ -326,6 +418,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
|
||||
data->input = input;
|
||||
data->display_externally_owned = display_is_external;
|
||||
WAYLAND_wl_list_init(&data->output_list);
|
||||
WAYLAND_wl_list_init(&data->output_order);
|
||||
WAYLAND_wl_list_init(&external_window_list);
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
@ -803,6 +896,10 @@ static void display_handle_scale(void *data,
|
||||
|
||||
static void display_handle_name(void *data, struct wl_output *wl_output, const char *name)
|
||||
{
|
||||
SDL_DisplayData *driverdata = (SDL_DisplayData *)data;
|
||||
|
||||
SDL_free(driverdata->wl_output_name);
|
||||
driverdata->wl_output_name = SDL_strdup(name);
|
||||
}
|
||||
|
||||
static void display_handle_description(void *data, struct wl_output *wl_output, const char *description)
|
||||
@ -859,6 +956,8 @@ static void Wayland_free_display(SDL_VideoDisplay *display)
|
||||
SDL_DisplayData *display_data = display->driverdata;
|
||||
int i;
|
||||
|
||||
SDL_free(display_data->wl_output_name);
|
||||
|
||||
if (display_data->xdg_output) {
|
||||
zxdg_output_v1_destroy(display_data->xdg_output);
|
||||
}
|
||||
@ -884,23 +983,9 @@ static void Wayland_free_display(SDL_VideoDisplay *display)
|
||||
static void Wayland_FinalizeDisplays(SDL_VideoData *vid)
|
||||
{
|
||||
SDL_DisplayData *d;
|
||||
int p_x, p_y;
|
||||
|
||||
/* GNOME doesn't expose the displays in any preferential order, so find the primary display coordinates and use them
|
||||
* to manually sort the primary display to the front of the list so that it is always the first exposed by SDL.
|
||||
* Otherwise, assume that the displays were already exposed in preferential order.
|
||||
* */
|
||||
if (Wayland_GetPrimaryDisplayCoordinates(&p_x, &p_y)) {
|
||||
wl_list_for_each (d, &vid->output_list, link) {
|
||||
if (d->x == p_x && d->y == p_y) {
|
||||
WAYLAND_wl_list_remove(&d->link);
|
||||
WAYLAND_wl_list_insert(&vid->output_list, &d->link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_for_each (d, &vid->output_list, link) {
|
||||
Wayland_SortOutputs(vid);
|
||||
wl_list_for_each(d, &vid->output_list, link) {
|
||||
d->display = SDL_AddVideoDisplay(&d->placeholder, SDL_FALSE);
|
||||
SDL_free(d->placeholder.name);
|
||||
SDL_zero(d->placeholder);
|
||||
@ -994,6 +1079,9 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
|
||||
if (d->input) {
|
||||
Wayland_RegisterTimestampListeners(d->input);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1223,6 +1311,12 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
|
||||
data->input_timestamps_manager = NULL;
|
||||
}
|
||||
|
||||
if (data->kde_output_order) {
|
||||
Wayland_FlushOutputOrder(data);
|
||||
kde_output_order_v1_destroy(data->kde_output_order);
|
||||
data->kde_output_order = NULL;
|
||||
}
|
||||
|
||||
if (data->compositor) {
|
||||
wl_compositor_destroy(data->compositor);
|
||||
data->compositor = NULL;
|
||||
|
@ -41,6 +41,12 @@ typedef struct
|
||||
int size;
|
||||
} SDL_WaylandCursorTheme;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct wl_list link;
|
||||
char wl_output_name[];
|
||||
} SDL_WaylandConnectorName;
|
||||
|
||||
struct SDL_VideoData
|
||||
{
|
||||
SDL_bool initializing;
|
||||
@ -72,11 +78,15 @@ struct SDL_VideoData
|
||||
struct wp_viewporter *viewporter;
|
||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
||||
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
|
||||
struct kde_output_order_v1 *kde_output_order;
|
||||
|
||||
struct xkb_context *xkb_context;
|
||||
struct SDL_WaylandInput *input;
|
||||
struct SDL_WaylandTabletManager *tablet_manager;
|
||||
struct wl_list output_list;
|
||||
struct wl_list output_order;
|
||||
|
||||
SDL_bool output_order_finalized;
|
||||
|
||||
int relative_mouse_mode;
|
||||
SDL_bool display_externally_owned;
|
||||
@ -87,6 +97,7 @@ struct SDL_DisplayData
|
||||
SDL_VideoData *videodata;
|
||||
struct wl_output *output;
|
||||
struct zxdg_output_v1 *xdg_output;
|
||||
char *wl_output_name;
|
||||
uint32_t registry_id;
|
||||
float scale_factor;
|
||||
int pixel_width, pixel_height;
|
||||
|
33
wayland-protocols/kde-output-order-v1.xml
Normal file
33
wayland-protocols/kde-output-order-v1.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="kde_output_order_v1">
|
||||
<copyright><![CDATA[
|
||||
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: MIT-CMU
|
||||
]]></copyright>
|
||||
|
||||
<interface name="kde_output_order_v1" version="1">
|
||||
<description summary="announce order of outputs">
|
||||
Announce the order in which desktop environment components should be placed on outputs.
|
||||
The compositor will send the list of outputs when the global is bound and whenever there is a change.
|
||||
</description>
|
||||
|
||||
<event name="output">
|
||||
<description summary="output name">
|
||||
Specifies the output identified by their wl_output.name.
|
||||
</description>
|
||||
<arg name="output_name" type="string" summary="the name of the output"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="done">
|
||||
Specifies that the output list is complete. On the next output event, a new list begins.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="Destroy the output order notifier."/>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
</protocol>
|
Loading…
Reference in New Issue
Block a user