Blob Blame History Raw
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