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.main
parent
e0c2cca629
commit
e71e16950a
|
@ -57,6 +57,7 @@
|
||||||
#include "input-timestamps-unstable-v1-client-protocol.h"
|
#include "input-timestamps-unstable-v1-client-protocol.h"
|
||||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "pointer-constraints-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
|
#ifdef HAVE_LIBDECOR_H
|
||||||
#include <libdecor.h>
|
#include <libdecor.h>
|
||||||
|
@ -96,7 +97,7 @@
|
||||||
/* GNOME doesn't expose displays in any particular order, but we can find the
|
/* GNOME doesn't expose displays in any particular order, but we can find the
|
||||||
* primary display and its logical coordinates via a DBus method.
|
* 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
|
#ifdef SDL_USE_LIBDBUS
|
||||||
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
SDL_DBusContext *dbus = SDL_DBus_GetContext();
|
||||||
|
@ -193,6 +194,97 @@ error:
|
||||||
return SDL_FALSE;
|
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);
|
static void display_handle_done(void *data, struct wl_output *output);
|
||||||
|
|
||||||
/* Initialization/Query functions */
|
/* Initialization/Query functions */
|
||||||
|
@ -326,6 +418,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
|
||||||
data->input = input;
|
data->input = input;
|
||||||
data->display_externally_owned = display_is_external;
|
data->display_externally_owned = display_is_external;
|
||||||
WAYLAND_wl_list_init(&data->output_list);
|
WAYLAND_wl_list_init(&data->output_list);
|
||||||
|
WAYLAND_wl_list_init(&data->output_order);
|
||||||
WAYLAND_wl_list_init(&external_window_list);
|
WAYLAND_wl_list_init(&external_window_list);
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
/* 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)
|
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)
|
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;
|
SDL_DisplayData *display_data = display->driverdata;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
SDL_free(display_data->wl_output_name);
|
||||||
|
|
||||||
if (display_data->xdg_output) {
|
if (display_data->xdg_output) {
|
||||||
zxdg_output_v1_destroy(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)
|
static void Wayland_FinalizeDisplays(SDL_VideoData *vid)
|
||||||
{
|
{
|
||||||
SDL_DisplayData *d;
|
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
|
Wayland_SortOutputs(vid);
|
||||||
* to manually sort the primary display to the front of the list so that it is always the first exposed by SDL.
|
wl_list_for_each(d, &vid->output_list, link) {
|
||||||
* 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) {
|
|
||||||
d->display = SDL_AddVideoDisplay(&d->placeholder, SDL_FALSE);
|
d->display = SDL_AddVideoDisplay(&d->placeholder, SDL_FALSE);
|
||||||
SDL_free(d->placeholder.name);
|
SDL_free(d->placeholder.name);
|
||||||
SDL_zero(d->placeholder);
|
SDL_zero(d->placeholder);
|
||||||
|
@ -994,6 +1079,9 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
|
||||||
if (d->input) {
|
if (d->input) {
|
||||||
Wayland_RegisterTimestampListeners(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;
|
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) {
|
if (data->compositor) {
|
||||||
wl_compositor_destroy(data->compositor);
|
wl_compositor_destroy(data->compositor);
|
||||||
data->compositor = NULL;
|
data->compositor = NULL;
|
||||||
|
|
|
@ -41,6 +41,12 @@ typedef struct
|
||||||
int size;
|
int size;
|
||||||
} SDL_WaylandCursorTheme;
|
} SDL_WaylandCursorTheme;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
struct wl_list link;
|
||||||
|
char wl_output_name[];
|
||||||
|
} SDL_WaylandConnectorName;
|
||||||
|
|
||||||
struct SDL_VideoData
|
struct SDL_VideoData
|
||||||
{
|
{
|
||||||
SDL_bool initializing;
|
SDL_bool initializing;
|
||||||
|
@ -72,11 +78,15 @@ struct SDL_VideoData
|
||||||
struct wp_viewporter *viewporter;
|
struct wp_viewporter *viewporter;
|
||||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
||||||
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
|
struct zwp_input_timestamps_manager_v1 *input_timestamps_manager;
|
||||||
|
struct kde_output_order_v1 *kde_output_order;
|
||||||
|
|
||||||
struct xkb_context *xkb_context;
|
struct xkb_context *xkb_context;
|
||||||
struct SDL_WaylandInput *input;
|
struct SDL_WaylandInput *input;
|
||||||
struct SDL_WaylandTabletManager *tablet_manager;
|
struct SDL_WaylandTabletManager *tablet_manager;
|
||||||
struct wl_list output_list;
|
struct wl_list output_list;
|
||||||
|
struct wl_list output_order;
|
||||||
|
|
||||||
|
SDL_bool output_order_finalized;
|
||||||
|
|
||||||
int relative_mouse_mode;
|
int relative_mouse_mode;
|
||||||
SDL_bool display_externally_owned;
|
SDL_bool display_externally_owned;
|
||||||
|
@ -87,6 +97,7 @@ struct SDL_DisplayData
|
||||||
SDL_VideoData *videodata;
|
SDL_VideoData *videodata;
|
||||||
struct wl_output *output;
|
struct wl_output *output;
|
||||||
struct zxdg_output_v1 *xdg_output;
|
struct zxdg_output_v1 *xdg_output;
|
||||||
|
char *wl_output_name;
|
||||||
uint32_t registry_id;
|
uint32_t registry_id;
|
||||||
float scale_factor;
|
float scale_factor;
|
||||||
int pixel_width, pixel_height;
|
int pixel_width, pixel_height;
|
||||||
|
|
|
@ -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