From 275fa02a15ebdb75b07c100de9395392f9543300 Mon Sep 17 00:00:00 2001
From: Mamoru TASAKA <mtasaka@fedoraproject.org>
Date: Mon, 20 Sep 2021 16:22:40 +0900
Subject: [PATCH] [LP1943052] wayland-manager: allocate new wl_output
information by checking id
With KDE Plasma Wayland session, after calling wl_output_add_listener()
and waiting for wl_output information with wl_display_roundtrip(),
wl_output::mode info is returned before wl_output::geometry info.
Current cairo-dock-wayland-manager logic expects the opposite order, and
on KDE session this causes cairo-dock segfault.
To avoid this, when collecting wl_output information, we should also check
"id" member of global registry object associated with that wl_output info,
check if the "id" is already the duplicates of the old infos.
Then if the "id" is new one, then allocate the new buffer to register new
wl_output information beforehand, then call wl_display_roundtrip() to gain
wl_output::geometry or wl_output::output information.
---
src/gldit/cairo-dock-desktop-manager.h | 13 +-
src/implementations/cairo-dock-X-utilities.c | 20 +--
.../cairo-dock-wayland-manager.c | 134 +++++++++++++++---
3 files changed, 139 insertions(+), 28 deletions(-)
diff --git a/src/gldit/cairo-dock-desktop-manager.h b/src/gldit/cairo-dock-desktop-manager.h
index b8ecb0d0..2774913b 100644
--- a/src/gldit/cairo-dock-desktop-manager.h
+++ b/src/gldit/cairo-dock-desktop-manager.h
@@ -59,10 +59,21 @@ typedef enum {
NB_NOTIFICATIONS_DESKTOP
} CairoDesktopNotifications;
+typedef struct GldiWlOutput {
+ struct wl_output *output;
+ uint32_t id, ver;
+} GldiWlOutput;
+
+typedef struct GldiScreenInfo {
+ int x, y;
+ int width, height;
+ GldiWlOutput wloutput; // wl_registry object identifier (in wayland session)
+} GldiScreenInfo;
+
// data
struct _GldiDesktopGeometry {
int iNbScreens;
- GtkAllocation *pScreens; // liste of all screen devices.
+ GldiScreenInfo *pScreens; // liste of all screen devices.
GtkAllocation Xscreen; // logical screen, possibly made of several screen devices.
int iNbDesktops;
int iNbViewportX, iNbViewportY;
diff --git a/src/implementations/cairo-dock-X-utilities.c b/src/implementations/cairo-dock-X-utilities.c
index 26217dab..9d39e017 100644
--- a/src/implementations/cairo-dock-X-utilities.c
+++ b/src/implementations/cairo-dock-X-utilities.c
@@ -89,7 +89,7 @@ static Atom s_aUtf8String;
static Atom s_aString;
static unsigned char error_code = Success;
-static GtkAllocation *_get_screens_geometry (int *pNbScreens);
+static GldiScreenInfo *_get_screens_geometry (int *pNbScreens);
static gboolean cairo_dock_support_X_extension (void);
@@ -185,14 +185,14 @@ unsigned char cairo_dock_get_X_error_code (void)
return error_code;
}
-static GtkAllocation *_get_screens_geometry (int *pNbScreens)
+static GldiScreenInfo *_get_screens_geometry (int *pNbScreens)
{
- GtkAllocation *pScreens = NULL;
- GtkAllocation *pScreen;
+ GldiScreenInfo *pScreens = NULL;
+ GldiScreenInfo *pScreen;
int iNbScreens = 0;
/*Unit Tests
iNbScreens = 2;
- pScreens = g_new0 (GtkAllocation, iNbScreens);
+ pScreens = g_new0 (GldiScreenInfo, iNbScreens);
pScreens[0].x = 0;
pScreens[0].y = 0;
pScreens[0].width = 1000;
@@ -213,7 +213,7 @@ static GtkAllocation *_get_screens_geometry (int *pNbScreens)
{
int n = res->ncrtc;
cd_debug (" number of screen(s): %d", n);
- pScreens = g_new0 (GtkAllocation, n);
+ pScreens = g_new0 (GldiScreenInfo, n);
int i;
for (i = 0; i < n; i++)
{
@@ -256,7 +256,7 @@ static GtkAllocation *_get_screens_geometry (int *pNbScreens)
if (scr != NULL)
{
cd_debug (" number of screen(s): %d", n);
- pScreens = g_new0 (GtkAllocation, n);
+ pScreens = g_new0 (GldiScreenInfo, n);
int i;
for (i = 0; i < n; i++)
{
@@ -286,7 +286,7 @@ static GtkAllocation *_get_screens_geometry (int *pNbScreens)
#endif
iNbScreens = 1;
- pScreens = g_new0 (GtkAllocation, iNbScreens);
+ pScreens = g_new0 (GldiScreenInfo, iNbScreens);
pScreen = &pScreens[0];
pScreen->x = 0;
pScreen->y = 0;
@@ -335,7 +335,7 @@ gboolean cairo_dock_update_screen_geometry (void)
}
// get the size and position of each screen (they could have changed even though the X screen has not changed, for instance if you swap 2 screens).
- GtkAllocation *pScreens = g_desktopGeometry.pScreens;
+ GldiScreenInfo *pScreens = g_desktopGeometry.pScreens;
int iNbScreens = g_desktopGeometry.iNbScreens;
g_desktopGeometry.pScreens = _get_screens_geometry (&g_desktopGeometry.iNbScreens);
@@ -347,7 +347,7 @@ gboolean cairo_dock_update_screen_geometry (void)
int i;
for (i = 0; i < MIN (iNbScreens, g_desktopGeometry.iNbScreens); i ++)
{
- if (memcmp (&pScreens[i], &g_desktopGeometry.pScreens[i], sizeof (GtkAllocation)) != 0)
+ if (memcmp (&pScreens[i], &g_desktopGeometry.pScreens[i], sizeof (GldiScreenInfo)) != 0)
{
bNewSize = TRUE;
break;
diff --git a/src/implementations/cairo-dock-wayland-manager.c b/src/implementations/cairo-dock-wayland-manager.c
index 9ce8d505..f9732133 100644
--- a/src/implementations/cairo-dock-wayland-manager.c
+++ b/src/implementations/cairo-dock-wayland-manager.c
@@ -73,31 +73,97 @@ struct desktop
};
static gboolean s_bInitializing = TRUE; // each time a callback is called on startup, it will set this to TRUE, and we'll make a roundtrip to the server until no callback is called.
-static void _output_geometry_cb (G_GNUC_UNUSED void *data, G_GNUC_UNUSED struct wl_output *wl_output,
+static int get_position_gldi_screeninfo_by_id (GldiScreenInfo *array_screens, int iNbScreens, uint32_t obj_id)
+{
+ int i_screen;
+ GldiScreenInfo *p_screen = array_screens;
+ for (i_screen = 0; i_screen < iNbScreens; i_screen++)
+ {
+ if (p_screen->wloutput.id == obj_id)
+ {
+ return i_screen;
+ }
+ p_screen++;
+ }
+
+ return -1;
+}
+
+static GldiScreenInfo *append_gldi_screeninfo(GldiScreenInfo **ref_array_screens, int *ref_iNbScreens)
+{
+ int iNbScreens_old = *ref_iNbScreens;
+ int iNbScreens_new = iNbScreens_old + 1;
+ GldiScreenInfo *p_new_screeninfo;
+
+ if (!*ref_array_screens)
+ *ref_array_screens = g_new(GldiScreenInfo, 1);
+ else
+ *ref_array_screens = g_realloc(*ref_array_screens, iNbScreens_new * sizeof (**ref_array_screens));
+ if (!*ref_array_screens) abort();
+
+ p_new_screeninfo = *ref_array_screens + (iNbScreens_new - 1);
+ *ref_iNbScreens = iNbScreens_new;
+
+ return p_new_screeninfo;
+}
+
+static void remove_gldi_screeninfo_by_id(GldiScreenInfo **ref_array_screens, int *ref_iNbScreens, uint32_t obj_id)
+{
+ int iNbScreens_old = *ref_iNbScreens;
+ int pos_screen;
+ GldiScreenInfo *p_screen;
+
+ if (iNbScreens_old <= 1) return; // Keep at least one information for now
+
+ pos_screen = get_position_gldi_screeninfo_by_id(*ref_array_screens, *ref_iNbScreens, obj_id);
+ if (pos_screen < 0) return;
+
+ p_screen = *ref_array_screens + pos_screen;
+#ifdef WL_OUTPUT_RELEASE_SINCE_VERSION
+ if (p_screen->wloutput.id >= WL_OUTPUT_RELEASE_SINCE_VERSION)
+ {
+ wl_output_release(p_screen->wloutput.output);
+ } else
+#endif
+ {
+ wl_output_destroy(p_screen->wloutput.output);
+ }
+
+ if (iNbScreens_old > pos_screen + 1)
+ {
+ memmove(
+ p_screen, p_screen + 1,
+ (iNbScreens_old - pos_screen - 1) * sizeof (**ref_array_screens)
+ );
+ }
+
+ (*ref_iNbScreens)--;
+}
+
+static void _output_geometry_cb (void *data, G_GNUC_UNUSED struct wl_output *wl_output,
int32_t x, int32_t y,
G_GNUC_UNUSED int32_t physical_width, G_GNUC_UNUSED int32_t physical_height,
G_GNUC_UNUSED int32_t subpixel, G_GNUC_UNUSED const char *make, G_GNUC_UNUSED const char *model, G_GNUC_UNUSED int32_t output_transform)
{
+ GldiScreenInfo *p_screeninfo = (GldiScreenInfo *)data;
cd_debug ("Geometry: %d;%d", x, y);
- g_desktopGeometry.iNbScreens ++;
- if (!g_desktopGeometry.pScreens)
- g_desktopGeometry.pScreens = g_new0 (GtkAllocation, 1);
- else
- g_desktopGeometry.pScreens = g_realloc (g_desktopGeometry.pScreens, g_desktopGeometry.iNbScreens * sizeof(GtkAllocation));
+ cd_debug ("id: %u", (unsigned int)p_screeninfo->wloutput.id);
- g_desktopGeometry.pScreens[g_desktopGeometry.iNbScreens-1].x = x;
- g_desktopGeometry.pScreens[g_desktopGeometry.iNbScreens-1].y = y;
+ p_screeninfo->x = x;
+ p_screeninfo->y = y;
s_bInitializing = TRUE;
}
-static void _output_mode_cb (G_GNUC_UNUSED void *data, G_GNUC_UNUSED struct wl_output *wl_output,
+static void _output_mode_cb (void *data, G_GNUC_UNUSED struct wl_output *wl_output,
uint32_t flags, int32_t width, int32_t height, G_GNUC_UNUSED int32_t refresh)
{
+ GldiScreenInfo *p_screeninfo = (GldiScreenInfo *)data;
cd_debug ("Output mode: %dx%d, %d", width, height, flags);
+ cd_debug ("id: %u", (unsigned int)p_screeninfo->wloutput.id);
if (flags & WL_OUTPUT_MODE_CURRENT) // not the current one -> don't bother
{
- g_desktopGeometry.pScreens[g_desktopGeometry.iNbScreens-1].width = width;
- g_desktopGeometry.pScreens[g_desktopGeometry.iNbScreens-1].height = height;
+ p_screeninfo->width = width;
+ p_screeninfo->height = height;
g_desktopGeometry.Xscreen.width = width;
g_desktopGeometry.Xscreen.height = height;
}
@@ -124,14 +190,29 @@ static void _output_scale_cb (G_GNUC_UNUSED void *data, G_GNUC_UNUSED struct wl_
static const struct wl_output_listener output_listener = {
_output_geometry_cb,
- _output_mode_cb,
+ _output_mode_cb
+#ifdef WL_OUTPUT_SCALE_SINCE_VERSION
+ ,
_output_done_cb,
_output_scale_cb
+#endif
};
-static void _registry_global_cb (G_GNUC_UNUSED void *data, struct wl_registry *registry, uint32_t id, const char *interface, G_GNUC_UNUSED uint32_t version)
+static void _registry_global_cb (G_GNUC_UNUSED void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)
{
- cd_debug ("got a new global object, instance of %s, id=%d", interface, id);
+ cd_debug ("got a new global object, instance of %s, id=%d, version=%u", interface, id, version);
+ uint32_t wl_output_version =
+#if defined(WL_OUTPUT_RELEASE_SINCE_VERSION)
+ WL_OUTPUT_RELEASE_SINCE_VERSION;
+#elif defined(WL_OUTPUT_SCALE_SINCE_VERSION)
+ WL_OUTPUT_SCALE_SINCE_VERSION;
+#else
+ 1;
+#endif
+ // server side may not support newer version, in such case suppress the version to use
+ if (wl_output_version > version)
+ wl_output_version = version;
+
if (!strcmp (interface, "wl_shell"))
{
// this is the global that should give us info and signals about the desktop, but currently it's pretty useless ...
@@ -142,10 +223,26 @@ static void _registry_global_cb (G_GNUC_UNUSED void *data, struct wl_registry *r
struct wl_output *output = wl_registry_bind (registry,
id,
&wl_output_interface,
- 1);
+ wl_output_version);
+
+ GldiScreenInfo *p_screeninfo = NULL;
+ int screen_num = get_position_gldi_screeninfo_by_id(g_desktopGeometry.pScreens, g_desktopGeometry.iNbScreens, id);
+ if (screen_num >= 0)
+ p_screeninfo = g_desktopGeometry.pScreens + screen_num;
+ else
+ p_screeninfo = append_gldi_screeninfo(&g_desktopGeometry.pScreens, &g_desktopGeometry.iNbScreens);
+
+ {
+ // register new wl_output
+ GldiWlOutput *p_wloutput = &p_screeninfo->wloutput;
+ p_wloutput->output = output;
+ p_wloutput->id = id;
+ p_wloutput->ver = wl_output_version;
+ }
+
wl_output_add_listener (output,
&output_listener,
- NULL);
+ p_screeninfo);
}
s_bInitializing = TRUE;
}
@@ -153,7 +250,7 @@ static void _registry_global_cb (G_GNUC_UNUSED void *data, struct wl_registry *r
static void _registry_global_remove_cb (G_GNUC_UNUSED void *data, G_GNUC_UNUSED struct wl_registry *registry, uint32_t id)
{
cd_debug ("got a global object has disappeared: id=%d", id);
- /// TODO: find it and destroy it...
+ remove_gldi_screeninfo_by_id(&g_desktopGeometry.pScreens, &g_desktopGeometry.iNbScreens, id);
/// TODO: and if it was a wl_output for instance, update the desktop geometry...
@@ -171,6 +268,9 @@ static void init (void)
s_pDisplay = wl_display_connect (NULL);
g_desktopGeometry.iNbDesktops = g_desktopGeometry.iNbViewportX = g_desktopGeometry.iNbViewportY = 1;
+ // Explicitly initialize below for readability
+ g_desktopGeometry.pScreens = NULL;
+ g_desktopGeometry.iNbScreens = 0;
struct wl_registry *registry = wl_display_get_registry (s_pDisplay);
--
2.31.1