CasildaCompositor: add socketpair support

- Add casilda_compositor_get_client_socket_fd()
 - Add casilda_compositor_spawn_async()

Closes issue #8 "Investigate using WAYLAND_SOCKET"
This commit is contained in:
Juan Pablo Ugarte 2025-06-25 00:04:04 -04:00
parent 0185440702
commit 1b8efba708
No known key found for this signature in database
3 changed files with 183 additions and 47 deletions

View file

@ -1,33 +1,47 @@
#include <gtk/gtk.h>
#include "casilda-compositor.h"
/* Do not listen on any socket by default */
gchar *socket = NULL;
static void
activate (GtkApplication *app,
G_GNUC_UNUSED gpointer user_data)
activate (GtkApplication *app, G_GNUC_UNUSED gpointer user_data)
{
CasildaCompositor *compositor;
GtkWidget *window;
g_autofree char *socket = NULL;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Compositor");
gtk_window_set_title (GTK_WINDOW (window), "Casilda Compositor");
gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
socket = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (),
"casilda-example.sock", NULL);
if (socket)
g_message ("Listening on %s", socket);
else
g_message ("Not listening in any socket, you can pass a socket path as parameter");
compositor = casilda_compositor_new (socket);
gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (compositor));
gtk_window_present (GTK_WINDOW (window));
gchar *argv[2] = {
"/usr/bin/gtk4-demo",
NULL
};
casilda_compositor_spawn_async (compositor, NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, NULL);
}
int
main (int argc,
char **argv)
main (int argc, char **argv)
{
g_autoptr(GtkApplication) app = NULL;
app = gtk_application_new ("org.gnome.casilda.compositor", G_APPLICATION_DEFAULT_FLAGS);
app = gtk_application_new ("ar.xjuan.casilda.Example", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
if (argc >= 2)
socket = argv[1];
return g_application_run (G_APPLICATION (app), 0, NULL);
}

View file

@ -29,6 +29,8 @@
#define ENABLE_GL_DEBUG 0
#define ENABLE_BORDERS 0
#include <sys/socket.h>
#include <sys/un.h>
#include <epoxy/egl.h>
#include <linux/input-event-codes.h>
#include <wayland-server-core.h>
@ -162,9 +164,10 @@ typedef struct
GdkTexture *cursor_gdk_texture;
GdkCursor *cursor_gdk_cursor;
struct sockaddr_un abstract_socket;
/* GObject properties */
gchar *socket;
gboolean owns_socket;
} CasildaCompositorPrivate;
@ -1675,14 +1678,6 @@ casilda_compositor_finalize (GObject *object)
CasildaCompositorPrivate *priv = GET_PRIVATE (object);
g_clear_pointer (&priv->toplevel_state, g_hash_table_destroy);
if (priv->owns_socket)
{
priv->owns_socket = FALSE;
g_autofree char *socket_path = g_path_get_basename (priv->socket);
g_unlink (priv->socket);
g_rmdir (socket_path);
}
g_clear_pointer (&priv->socket, g_free);
wl_display_destroy_clients (priv->wl_display);
@ -1713,7 +1708,6 @@ casilda_compositor_set_property (GObject *object,
{
case PROP_SOCKET:
g_set_str (&priv->socket, g_value_get_string (value));
priv->owns_socket = priv->socket != NULL;
break;
case PROP_BG_COLOR:
@ -1807,9 +1801,12 @@ casilda_compositor_class_init (CasildaCompositorClass *klass)
*
* Wayland socket the compositor will listen on.
*
* If NULL you can still use WAYLAND_SOCKET with an fd returned by
* casilda_compositor_get_client_socket_fd() instead of setting up WAYLAND_DISPLAY.
*
*/
properties[PROP_SOCKET] =
g_param_spec_string ("socket", "", "", NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
g_param_spec_string ("socket", "", "", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
/**
* CasildaCompositor:bg-color:
@ -1824,13 +1821,6 @@ casilda_compositor_class_init (CasildaCompositorClass *klass)
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
}
CasildaCompositor *
casilda_compositor_new (const gchar *socket)
{
return g_object_new (CASILDA_COMPOSITOR_TYPE, "socket", socket, NULL);
}
/* wlroots */
static void
@ -2235,15 +2225,6 @@ server_request_activate (struct wl_listener *listener, void *data)
}
}
static gchar *
casilda_compositor_get_socket (void)
{
g_autofree char *tmp = g_dir_make_tmp ("casilda-compositor-XXXXXX", NULL);
g_autofree char *retval = g_build_filename (tmp, "wayland.sock", NULL);
return g_steal_pointer (&retval);
}
#if ENABLE_GL_DEBUG
static void GLAPIENTRY
on_gl_message_callback (G_GNUC_UNUSED GLenum source,
@ -2388,21 +2369,150 @@ casilda_compositor_wlr_init (CasildaCompositorPrivate *priv)
/* Configure seat */
priv->seat = wlr_seat_create (priv->wl_display, "seat0");
priv->request_set_selection.notify = seat_request_set_selection;
wl_signal_add (&priv->seat->events.request_set_selection,
&priv->request_set_selection);
wl_signal_add (&priv->seat->events.request_set_selection, &priv->request_set_selection);
wlr_seat_set_capabilities (priv->seat,
WL_SEAT_CAPABILITY_POINTER |
WL_SEAT_CAPABILITY_KEYBOARD);
wlr_seat_set_capabilities (priv->seat, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
if (!priv->socket)
{
priv->socket = casilda_compositor_get_socket ();
priv->owns_socket = TRUE;
}
return;
if (wl_display_add_socket (priv->wl_display, priv->socket) != 0)
if (wl_display_add_socket (priv->wl_display, priv->socket))
g_warning ("Error adding socket file %s", priv->socket);
else
g_info ("Listening on %s", priv->socket);
}
/* Public API */
CasildaCompositor *
casilda_compositor_new (const gchar *socket)
{
return g_object_new (CASILDA_COMPOSITOR_TYPE, "socket", socket, NULL);
}
/**
* casilda_compositor_get_client_socket_fd:
* @compositor: a `CasildaCompositor`
*
* Create a client socket file descriptor connected to this compositor ready to use by passing it to a client with
* WAYLAND_SOCKET environment variable.
*
* Returns: a socket FD connected to the compositor
*/
int
casilda_compositor_get_client_socket_fd (CasildaCompositor *compositor)
{
g_return_val_if_fail (CASILDA_IS_COMPOSITOR (compositor), -1);
CasildaCompositorPrivate *priv = GET_PRIVATE (compositor);
gint fds[2] = {0, };
if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds))
{
g_warning ("Could not create a UNIX socketpair");
return -1;
}
if (!wl_client_create (priv->wl_display, fds[0]))
{
g_warning ("Could not add socketpair to display");
return -1;
}
return fds[1];
}
/**
* casilda_compositor_spawn_async:
* @compositor: a `CasildaCompositor`
* @working_directory: (type filename) (nullable): child's current working directory, or %NULL to inherit parent's
* @argv: (array zero-terminated=1) (element-type filename): child's argument vector
* @envp: (array zero-terminated=1) (element-type filename) (nullable): child's environment, or %NULL to inherit parent's
* @flags: flags from #GSpawnFlags
* @child_setup: (scope async) (closure user_data) (nullable): function to run in the child just before `exec()`
* @user_data: user data for @child_setup
* @child_pid: (out) (optional): return location for child process reference, or %NULL
* @error: return location for error
*
* Executes a child program asynchronously with the right environment to automatically connect to this compositor.
*
* See g_spawn_async_with_pipes_and_fds() for a full description; this function simply calls
* g_spawn_async_with_pipes_and_fds() without any pipes and with WAYLAND_DISPLAY set to `CasildaCompositor::socket` or
* a fd already connected to the compositor and set to WAYLAND_SOCKET if its NULL.
*
* Please note GDK_BACKEND, WAYLAND_DISPLAY and WAYLAND_SOCKET are set by this function they are ignored from %envp and
* DISPLAY is removed to avoid clients try to connect using X11
*
* Returns: TRUE on success, FALSE if error is set.
*/
gboolean
casilda_compositor_spawn_async (CasildaCompositor *compositor,
const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GPid *child_pid,
GError **error)
{
g_return_val_if_fail (CASILDA_IS_COMPOSITOR (compositor), FALSE);
CasildaCompositorPrivate *priv = GET_PRIVATE (compositor);
g_autoptr(GStrvBuilder) builder = g_strv_builder_new ();
g_autofree gchar *wayland_socket = NULL;
g_auto(GStrv) environ = NULL;
GStrv source_env = NULL;
gint fd;
if (envp)
source_env = envp;
else
source_env = environ = g_get_environ();
/* Build environment */
for (gint i = 0; source_env[i]; i++)
{
if (g_str_has_prefix (source_env[i], "DISPLAY="))
continue;
if (g_str_has_prefix (source_env[i], "GDK_BACKEND="))
continue;
if (g_str_has_prefix (source_env[i], "WAYLAND_DISPLAY="))
continue;
if (g_str_has_prefix (source_env[i], "WAYLAND_SOCKET="))
continue;
g_strv_builder_add (builder, source_env[i]);
}
/* Force GTK apps to wayland backend */
g_strv_builder_add (builder, "GDK_BACKEND=wayland");
if (priv->socket)
wayland_socket = g_strdup_printf("WAYLAND_DISPLAY=%s", priv->socket);
else
{
fd = casilda_compositor_get_client_socket_fd (compositor);
wayland_socket = g_strdup_printf("WAYLAND_SOCKET=%d", fd);
}
/* Add wayland socket env var */
g_strv_builder_add (builder, wayland_socket);
g_auto(GStrv) env = g_strv_builder_end (builder);
return g_spawn_async_with_pipes_and_fds (working_directory,
(const gchar* const* ) argv,
(const gchar* const* ) env,
flags,
child_setup,
user_data,
-1, -1, -1,
&fd, &fd, priv->socket ? 0 : 1,
child_pid,
NULL, NULL, NULL,
error);
}

View file

@ -31,3 +31,15 @@
G_DECLARE_FINAL_TYPE (CasildaCompositor, casilda_compositor, CASILDA, COMPOSITOR, GtkWidget)
CasildaCompositor *casilda_compositor_new (const gchar *socket);
int casilda_compositor_get_client_socket_fd (CasildaCompositor *compositor);
gboolean casilda_compositor_spawn_async (CasildaCompositor *compositor,
const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GPid *child_pid,
GError **error);