/* * Copyright (C) 2018 Purism SPC * SPDX-License-Identifier: GPL-3.0+ * Author: Guido Günther */ /* WARNING: this file is taken directly from phosh, with no modificaions apart from this message. Please update phosh instead of changing this file. Please copy the file back here afterwards, with the same notice. */ #define G_LOG_DOMAIN "phosh-layer-surface" #include "config.h" #include "layersurface.h" #include enum { PHOSH_LAYER_SURFACE_PROP_0, PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL, PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT, PHOSH_LAYER_SURFACE_PROP_ANCHOR, PHOSH_LAYER_SURFACE_PROP_LAYER, PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY, PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE, PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP, PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM, PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT, PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT, PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH, PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT, PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH, PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT, PHOSH_LAYER_SURFACE_PROP_NAMESPACE, PHOSH_LAYER_SURFACE_PROP_LAST_PROP }; static GParamSpec *props[PHOSH_LAYER_SURFACE_PROP_LAST_PROP]; enum { CONFIGURED, N_SIGNALS }; static guint signals [N_SIGNALS]; typedef struct { struct wl_surface *wl_surface; struct zwlr_layer_surface_v1 *layer_surface; /* Properties */ guint anchor; guint layer; gboolean kbd_interactivity; gint exclusive_zone; gint margin_top, margin_bottom; gint margin_left, margin_right; gint width, height; gint configured_width, configured_height; gchar *namespace; struct zwlr_layer_shell_v1 *layer_shell; struct wl_output *wl_output; } PhoshLayerSurfacePrivate; G_DEFINE_TYPE_WITH_PRIVATE (PhoshLayerSurface, phosh_layer_surface, GTK_TYPE_WINDOW) static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { PhoshLayerSurface *self = data; PhoshLayerSurfacePrivate *priv; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); gtk_window_resize (GTK_WINDOW (self), width, height); zwlr_layer_surface_v1_ack_configure(surface, serial); if (priv->configured_height != height) { priv->configured_height = height; g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT]); } if (priv->configured_width != width) { priv->configured_width = width; g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH]); } g_debug("Configured %p", self); g_signal_emit (self, signals[CONFIGURED], 0); } static void layer_surface_closed (void *data, struct zwlr_layer_surface_v1 *surface) { PhoshLayerSurface *self = data; PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); g_return_if_fail (priv->layer_surface == surface); zwlr_layer_surface_v1_destroy(priv->layer_surface); priv->layer_surface = NULL; gtk_widget_destroy (GTK_WIDGET (self)); } static struct zwlr_layer_surface_v1_listener layer_surface_listener = { .configure = layer_surface_configure, .closed = layer_surface_closed, }; static void phosh_layer_surface_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object); PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); gint width, height; switch (property_id) { case PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL: priv->layer_shell = g_value_get_pointer (value); break; case PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT: priv->wl_output = g_value_get_pointer (value); break; case PHOSH_LAYER_SURFACE_PROP_ANCHOR: priv->anchor = g_value_get_uint (value); break; case PHOSH_LAYER_SURFACE_PROP_LAYER: priv->layer = g_value_get_uint (value); break; case PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY: phosh_layer_surface_set_kbd_interactivity (self, g_value_get_boolean (value)); break; case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE: phosh_layer_surface_set_exclusive_zone (self, g_value_get_int (value)); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP: phosh_layer_surface_set_margins (self, g_value_get_int (value), priv->margin_right, priv->margin_bottom, priv->margin_left); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM: phosh_layer_surface_set_margins (self, priv->margin_top, priv->margin_right, g_value_get_int (value), priv->margin_left); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT: phosh_layer_surface_set_margins (self, priv->margin_top, priv->margin_right, priv->margin_bottom, g_value_get_int (value)); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT: phosh_layer_surface_set_margins (self, priv->margin_top, g_value_get_int (value), priv->margin_bottom, priv->margin_left); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH: width = g_value_get_uint (value); phosh_layer_surface_set_size(self, width, priv->height); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT: height = g_value_get_uint (value); phosh_layer_surface_set_size(self, priv->width, height); break; case PHOSH_LAYER_SURFACE_PROP_NAMESPACE: g_free (priv->namespace); priv->namespace = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void phosh_layer_surface_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object); PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); switch (property_id) { case PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL: g_value_set_pointer (value, priv->layer_shell); break; case PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT: g_value_set_pointer (value, priv->wl_output); break; case PHOSH_LAYER_SURFACE_PROP_ANCHOR: g_value_set_uint (value, priv->anchor); break; case PHOSH_LAYER_SURFACE_PROP_LAYER: g_value_set_uint (value, priv->layer); break; case PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY: g_value_set_boolean (value, priv->kbd_interactivity); break; case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE: g_value_set_int (value, priv->exclusive_zone); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP: g_value_set_int (value, priv->margin_top); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM: g_value_set_int (value, priv->margin_bottom); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT: g_value_set_int (value, priv->margin_left); break; case PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT: g_value_set_int (value, priv->margin_right); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH: g_value_set_uint (value, priv->width); break; case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT: g_value_set_uint (value, priv->height); break; case PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH: g_value_set_uint (value, priv->configured_width); break; case PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT: g_value_set_uint (value, priv->configured_height); break; case PHOSH_LAYER_SURFACE_PROP_NAMESPACE: g_value_set_string (value, priv->namespace); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void on_phosh_layer_surface_realized (PhoshLayerSurface *self, gpointer unused) { PhoshLayerSurfacePrivate *priv; GdkWindow *gdk_window; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); gdk_window = gtk_widget_get_window (GTK_WIDGET (self)); gdk_wayland_window_set_use_custom_surface (gdk_window); priv->wl_surface = gdk_wayland_window_get_wl_surface (gdk_window); gtk_window_set_decorated (GTK_WINDOW (self), FALSE); } static void on_phosh_layer_surface_mapped (PhoshLayerSurface *self, gpointer unused) { PhoshLayerSurfacePrivate *priv; GdkWindow *gdk_window; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); if (!priv->wl_surface) { gdk_window = gtk_widget_get_window (GTK_WIDGET (self)); gdk_wayland_window_set_use_custom_surface (gdk_window); priv->wl_surface = gdk_wayland_window_get_wl_surface (gdk_window); } g_debug ("Mapped %p", priv->wl_surface); priv->layer_surface = zwlr_layer_shell_v1_get_layer_surface(priv->layer_shell, priv->wl_surface, priv->wl_output, priv->layer, priv->namespace); zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, priv->exclusive_zone); zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height); zwlr_layer_surface_v1_set_anchor(priv->layer_surface, priv->anchor); zwlr_layer_surface_v1_set_margin(priv->layer_surface, priv->margin_top, priv->margin_right, priv->margin_bottom, priv->margin_left); zwlr_layer_surface_v1_set_keyboard_interactivity(priv->layer_surface, priv->kbd_interactivity); zwlr_layer_surface_v1_add_listener(priv->layer_surface, &layer_surface_listener, self); wl_surface_commit(priv->wl_surface); /* Process all pending events, otherwise we end up sending ack configure * to a not yet configured surface */ wl_display_roundtrip (gdk_wayland_display_get_wl_display (gdk_display_get_default ())); } static void on_phosh_layer_surface_unmapped (PhoshLayerSurface *self, gpointer unused) { PhoshLayerSurfacePrivate *priv; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); if (priv->layer_surface) { zwlr_layer_surface_v1_destroy(priv->layer_surface); priv->layer_surface = NULL; } priv->wl_surface = NULL; } static void phosh_layer_surface_constructed (GObject *object) { PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object); g_signal_connect (self, "realize", G_CALLBACK (on_phosh_layer_surface_realized), NULL); g_signal_connect (self, "map", G_CALLBACK (on_phosh_layer_surface_mapped), NULL); g_signal_connect (self, "unmap", G_CALLBACK (on_phosh_layer_surface_unmapped), NULL); G_OBJECT_CLASS (phosh_layer_surface_parent_class)->constructed (object); } static void phosh_layer_surface_dispose (GObject *object) { PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object); PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self); if (priv->layer_surface) { zwlr_layer_surface_v1_destroy(priv->layer_surface); priv->layer_surface = NULL; } g_clear_pointer (&priv->namespace, g_free); G_OBJECT_CLASS (phosh_layer_surface_parent_class)->dispose (object); } static void phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass) { GObjectClass *object_class = (GObjectClass *)klass; object_class->constructed = phosh_layer_surface_constructed; object_class->dispose = phosh_layer_surface_dispose; object_class->set_property = phosh_layer_surface_set_property; object_class->get_property = phosh_layer_surface_get_property; props[PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL] = g_param_spec_pointer ( "layer-shell", "Wayland Layer Shell Global", "The layer shell wayland global", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT] = g_param_spec_pointer ( "wl-output", "Wayland Output", "The wl_output associated with this surface", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PHOSH_LAYER_SURFACE_PROP_ANCHOR] = g_param_spec_uint ( "anchor", "Anchor edges", "The edges to anchor the surface to", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PHOSH_LAYER_SURFACE_PROP_LAYER] = g_param_spec_uint ( "layer", "Layer", "The layer the surface should be attached to", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY] = g_param_spec_boolean ( "kbd-interactivity", "Keyboard interactivity", "Whether the surface interacts with the keyboard", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE] = g_param_spec_int ( "exclusive-zone", "Exclusive Zone", "Set area that is not occluded with other surfaces", -1, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT] = g_param_spec_int ( "margin-left", "Left margin", "Distance away from the left anchor point", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT] = g_param_spec_int ( "margin-right", "Right margin", "Distance away from the right anchor point", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP] = g_param_spec_int ( "margin-top", "Top margin", "Distance away from the top anchor point", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM] = g_param_spec_int ( "margin-bottom", "Bottom margin", "Distance away from the bottom anchor point", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH] = g_param_spec_uint ( "width", "Width", "The width of the layer surface", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT] = g_param_spec_uint ( "height", "Height", "The height of the layer surface", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_WIDTH] = g_param_spec_uint ( "configured-width", "Configured width", "The width of the layer surface set by the compositor", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_CONFIGURED_HEIGHT] = g_param_spec_uint ( "configured-height", "Configured height", "The height of the layer surface set by the compositor", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); props[PHOSH_LAYER_SURFACE_PROP_NAMESPACE] = g_param_spec_string ( "namespace", "Namespace", "Namespace of the layer surface", "", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PHOSH_LAYER_SURFACE_PROP_LAST_PROP, props); /** * PhoshLayersurface::configured * @self: The #PhoshLayersurface instance. * * This signal is emitted once we received the configure event from the * compositor. */ signals[CONFIGURED] = g_signal_new ("configured", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (PhoshLayerSurfaceClass, configured), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void phosh_layer_surface_init (PhoshLayerSurface *self) { } GtkWidget * phosh_layer_surface_new (gpointer layer_shell, gpointer wl_output) { return g_object_new (PHOSH_TYPE_LAYER_SURFACE, "layer-shell", layer_shell, "wl-output", wl_output); } /** * phosh_layer_surface_get_surface: * * Get the layer layer surface or #NULL if the window * is not yet realized. */ struct zwlr_layer_surface_v1 * phosh_layer_surface_get_layer_surface(PhoshLayerSurface *self) { PhoshLayerSurfacePrivate *priv; g_return_val_if_fail (PHOSH_IS_LAYER_SURFACE (self), NULL); priv = phosh_layer_surface_get_instance_private (self); return priv->layer_surface; } /** * phosh_layer_surface_get_wl_surface: * * Get the layer wayland surface or #NULL if the window * is not yet realized. */ struct wl_surface * phosh_layer_surface_get_wl_surface(PhoshLayerSurface *self) { PhoshLayerSurfacePrivate *priv; g_return_val_if_fail (PHOSH_IS_LAYER_SURFACE (self), NULL); priv = phosh_layer_surface_get_instance_private (self); return priv->wl_surface; } /** * phosh_layer_surface_set_size: * * Set the size of a layer surface. A value of '-1' indicates 'use old value' */ void phosh_layer_surface_set_size(PhoshLayerSurface *self, gint width, gint height) { PhoshLayerSurfacePrivate *priv; gint old_width, old_height; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); if (priv->height == height && priv->width == width) { return; } old_width = priv->width; old_height = priv->height; if (width != -1) { priv->width = width; } if (height != -1) { priv->height = height; } if (gtk_widget_get_mapped (GTK_WIDGET (self))) { zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height); } if (priv->height != old_height) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT]); } if (priv->width != old_width) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH]); } } /** * phosh_layer_surface_set_margins: * * Set anchor margins of a layer surface. */ void phosh_layer_surface_set_margins(PhoshLayerSurface *self, gint top, gint right, gint bottom, gint left) { PhoshLayerSurfacePrivate *priv; gint old_top, old_bottom, old_left, old_right; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); old_top = priv->margin_top; old_left = priv->margin_left; old_right = priv->margin_right; old_bottom = priv->margin_bottom; if (old_top == top && old_left == left && old_right == right && old_bottom == bottom) { return; } priv->margin_top = top; priv->margin_left = left; priv->margin_right = right; priv->margin_bottom = bottom; if (priv->layer_surface) { zwlr_layer_surface_v1_set_margin(priv->layer_surface, top, right, bottom, left); } if (old_top != top) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_TOP]); } if (old_bottom != bottom) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_BOTTOM]); } if (old_left != left) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_LEFT]); } if (old_right != right) { g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_MARGIN_RIGHT]); } } /** * phosh_layer_surface_set_exclusive_zone: * * Set exclusive zone of a layer surface. */ void phosh_layer_surface_set_exclusive_zone(PhoshLayerSurface *self, gint zone) { PhoshLayerSurfacePrivate *priv; gint old_zone; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); old_zone = priv->exclusive_zone; if (old_zone == zone) { return; } priv->exclusive_zone = zone; if (priv->layer_surface) { zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, zone); } g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE]); } /** * phosh_layer_surface_set_keyboard_interactivity: * * Set keyboard ineractivity a layer surface. */ void phosh_layer_surface_set_kbd_interactivity (PhoshLayerSurface *self, gboolean interactivity) { PhoshLayerSurfacePrivate *priv; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); if (priv->kbd_interactivity == interactivity) { return; } priv->kbd_interactivity = interactivity; if (priv->layer_surface) { zwlr_layer_surface_v1_set_keyboard_interactivity (priv->layer_surface, interactivity); } g_object_notify_by_pspec (G_OBJECT (self), props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY]); } /** * phosh_layer_surface_wl_surface_commit: * * Forces a commit of layer surface's state. */ void phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self) { PhoshLayerSurfacePrivate *priv; g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); priv = phosh_layer_surface_get_instance_private (self); if (priv->wl_surface) { wl_surface_commit (priv->wl_surface); } }