CasildaCompositor: implement scrollable iface

Close issue #7 "Implement GtkScrollable iface"
This commit is contained in:
Juan Pablo Ugarte 2025-07-26 17:05:01 -04:00
parent 401deca8b3
commit ae56ab59af
No known key found for this signature in database
2 changed files with 176 additions and 56 deletions

View file

@ -9,7 +9,7 @@ static void
activate (GtkApplication *app, G_GNUC_UNUSED gpointer user_data)
{
CasildaCompositor *compositor;
GtkWidget *window;
GtkWidget *window, *sw;
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Casilda Compositor");
@ -20,8 +20,11 @@ activate (GtkApplication *app, G_GNUC_UNUSED gpointer user_data)
else
g_message ("Not listening in any socket, you can pass a socket path as parameter");
sw = gtk_scrolled_window_new ();
compositor = casilda_compositor_new (socket);
gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (compositor));
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), GTK_WIDGET (compositor));
gtk_window_set_child (GTK_WINDOW (window), GTK_WIDGET (sw));
gtk_window_present (GTK_WINDOW (window));
gchar *argv[2] = {

View file

@ -169,6 +169,8 @@ typedef struct
/* GObject properties */
gchar *socket;
GtkAdjustment *adjustment[2];
} CasildaCompositorPrivate;
@ -256,12 +258,19 @@ enum {
PROP_SOCKET,
PROP_BG_COLOR,
N_PROPERTIES
N_PROPERTIES,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
PROP_VSCROLL_POLICY
};
static GParamSpec *properties[N_PROPERTIES];
G_DEFINE_TYPE_WITH_PRIVATE (CasildaCompositor, casilda_compositor, GTK_TYPE_WIDGET);
G_DEFINE_TYPE_WITH_CODE (CasildaCompositor, casilda_compositor, GTK_TYPE_WIDGET,
G_ADD_PRIVATE (CasildaCompositor)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
#define GET_PRIVATE(d) ((CasildaCompositorPrivate *) casilda_compositor_get_instance_private ((CasildaCompositor *) d))
@ -449,8 +458,8 @@ wlr_texture_as_gdk (WlrTexture *texture, GdkGLContext *gl_context, pixman_region
static gboolean
xdg_surface_get_data (struct wlr_xdg_surface *xdg_surface, struct wlr_surface **surface, struct wlr_box *geometry)
{
if (!xdg_surface || !(*surface = xdg_surface->surface) || !xdg_surface->surface->buffer)
return TRUE;
if (!xdg_surface || !(*surface = xdg_surface->surface) || !xdg_surface->surface->buffer)
return TRUE;
if (geometry)
wlr_xdg_surface_get_geometry (xdg_surface, geometry);
@ -477,10 +486,10 @@ debug_snapshot_border (GtkSnapshot *snapshot,
#endif
typedef struct {
GdkGLContext *gl_context;
GtkSnapshot *snapshot;
GdkGLContext *gl_context;
gfloat scale;
struct timespec *now;
struct timespec now;
} SnapshotData;
static void
@ -506,34 +515,22 @@ snapshot_surfaces (struct wlr_surface *surface, int sx, int sy, void *data)
gdk_paintable_snapshot (GDK_PAINTABLE (tex), sdata->snapshot, w, h);
gtk_snapshot_restore (sdata->snapshot);
wlr_surface_send_frame_done (surface, sdata->now);
wlr_surface_send_frame_done (surface, &sdata->now);
g_set_object (&surface->data, tex);
}
static void
snapshot_surface (GtkSnapshot *snapshot,
GdkGLContext *gl_context,
struct timespec *now,
double scale,
struct wlr_surface *surface,
gint x,
gint y)
snapshot_surface (struct wlr_surface *surface, SnapshotData *data, gint x, gint y)
{
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
wlr_surface_for_each_surface (surface, snapshot_surfaces, &(SnapshotData) {gl_context, snapshot, scale, now });
gtk_snapshot_restore (snapshot);
gtk_snapshot_save (data->snapshot);
gtk_snapshot_translate (data->snapshot, &GRAPHENE_POINT_INIT (x, y));
wlr_surface_for_each_surface (surface, snapshot_surfaces, data);
gtk_snapshot_restore (data->snapshot);
}
static void
casilda_compositor_snapshot_popup (CasildaCompositorPopup *popup,
GtkSnapshot *snapshot,
GdkGLContext *gl_context,
struct timespec *now,
double scale,
gint parent_x,
gint parent_y)
casilda_compositor_snapshot_popup (CasildaCompositorPopup *popup, SnapshotData *data, gint parent_x, gint parent_y)
{
struct wlr_surface *psurface;
struct wlr_box pbox = {0, };
@ -544,19 +541,15 @@ casilda_compositor_snapshot_popup (CasildaCompositorPopup *popup,
gint popup_x = parent_x + popup->xdg_popup->current.geometry.x;
gint popup_y = parent_y + popup->xdg_popup->current.geometry.y;
snapshot_surface (snapshot, gl_context, now, scale, psurface, popup_x - pbox.x, popup_y - pbox.y);
snapshot_surface (psurface, data, popup_x - pbox.x, popup_y - pbox.y);
for (GList *l = popup->popups; l; l = g_list_next (l))
casilda_compositor_snapshot_popup (l->data, snapshot, gl_context, now, scale, popup_x, popup_y);
casilda_compositor_snapshot_popup (l->data, data, popup_x, popup_y);
}
static void
casilda_compositor_snapshot_toplevel (CasildaCompositorToplevel *toplevel,
GtkSnapshot *snapshot,
GdkGLContext *gl_context,
struct timespec *now,
double scale)
casilda_compositor_snapshot_toplevel (CasildaCompositorToplevel *toplevel, SnapshotData *data)
{
struct wlr_surface *surface;
struct wlr_box tbox = {0, };
@ -580,40 +573,51 @@ casilda_compositor_snapshot_toplevel (CasildaCompositorToplevel *toplevel,
});
graphene_vec4_init (&offset, 0, 0, 0, 0);
gtk_snapshot_push_color_matrix (snapshot, &matrix, &offset);
gtk_snapshot_push_color_matrix (data->snapshot, &matrix, &offset);
}
snapshot_surface (snapshot, gl_context, now, scale, surface, toplevel->x - tbox.x, toplevel->y - tbox.y);
snapshot_surface (surface, data, toplevel->x - tbox.x, toplevel->y - tbox.y);
/* Recurse toplevel popups */
for (GList *l = toplevel->popups; l; l = g_list_next (l))
casilda_compositor_snapshot_popup (l->data, snapshot, gl_context, now, scale, toplevel->x, toplevel->y);
casilda_compositor_snapshot_popup (l->data, data, toplevel->x, toplevel->y);
if (has_transient)
gtk_snapshot_pop (snapshot);
gtk_snapshot_pop (data->snapshot);
/* Recurse toplevel transient windows */
for (GList *l = toplevel->toplevels; l; l = g_list_next (l))
casilda_compositor_snapshot_toplevel (l->data, snapshot, gl_context, now, scale);
casilda_compositor_snapshot_toplevel (l->data, data);
}
static void
casilda_compositor_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
{
double scale = gdk_surface_get_scale (gtk_native_get_surface (gtk_widget_get_native (widget)));
CasildaCompositorPrivate *priv = GET_PRIVATE (widget);
struct timespec now;
clock_gettime (CLOCK_MONOTONIC, &now);
SnapshotData data = { snapshot, priv->gl_context, 1.0, {0, } };
gint x, y;
data.scale = gdk_surface_get_scale (gtk_native_get_surface (gtk_widget_get_native (widget)));
clock_gettime (CLOCK_MONOTONIC, &data.now);
/* Translate by scrollable offset */
x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (-x, -y));
/* Iterate all toplevels */
for (GList *l = g_list_last (priv->toplevels); l; l = g_list_previous (l))
casilda_compositor_snapshot_toplevel (l->data, snapshot, priv->gl_context, &now, scale);
casilda_compositor_snapshot_toplevel (l->data, &data);
if (priv->frame_clock_updating)
{
gdk_frame_clock_end_updating (priv->frame_clock);
priv->frame_clock_updating = FALSE;
}
gtk_snapshot_restore (snapshot);
}
static void
@ -628,6 +632,45 @@ casilda_compositor_queue_draw (CasildaCompositorPrivate *priv)
gtk_widget_queue_draw (priv->self);
}
static void compositor_update_adjustment_toplevel (CasildaCompositorPrivate *priv, CasildaCompositorToplevel *toplevel);
static void
compositor_update_adjustment (CasildaCompositorPrivate *priv, GList *toplevels)
{
for (GList *l = toplevels; l; l = g_list_next (l))
{
CasildaCompositorToplevel *toplevel = l->data;
compositor_update_adjustment_toplevel (priv, toplevel);
if (toplevel->toplevels)
compositor_update_adjustment (priv, toplevel->toplevels);
}
}
static void
compositor_update_adjustment_toplevel (CasildaCompositorPrivate *priv, CasildaCompositorToplevel *toplevel)
{
struct wlr_box tbox = {0, };
if (!toplevel->xdg_toplevel)
return;
wlr_xdg_surface_get_geometry (toplevel->xdg_toplevel->base, &tbox);
gint w = toplevel->x + tbox.width;
gint h = toplevel->y + tbox.height;
if (w > gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]))
gtk_adjustment_set_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], w);
if (h > gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]))
gtk_adjustment_set_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL], h);
if (toplevel->toplevels)
compositor_update_adjustment (priv, toplevel->toplevels);
}
static void
casilda_compositor_size_allocate (GtkWidget *widget, int w, int h, G_GNUC_UNUSED int b)
{
@ -638,6 +681,17 @@ casilda_compositor_size_allocate (GtkWidget *widget, int w, int h, G_GNUC_UNUSED
w = w * scale;
h = h * scale;
/* Update scrollable values */
gtk_adjustment_set_page_size (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], w);
gtk_adjustment_set_page_size (priv->adjustment[GTK_ORIENTATION_VERTICAL], h);
gtk_adjustment_set_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], w);
gtk_adjustment_set_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL], h);
compositor_update_adjustment (priv, priv->toplevels);
w = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
h = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
wlr_output_state_init (&state);
wlr_output_state_set_enabled (&state, true);
wlr_output_state_set_custom_mode (&state, w, h, 0);
@ -649,6 +703,7 @@ casilda_compositor_size_allocate (GtkWidget *widget, int w, int h, G_GNUC_UNUSED
for (GList *l = priv->toplevels; l; l = g_list_next (l))
{
CasildaCompositorToplevel *toplevel = l->data;
if (toplevel->xdg_toplevel->current.maximized || toplevel->xdg_toplevel->current.fullscreen)
casilda_compositor_toplevel_configure (toplevel, 0, 0, w, h);
}
@ -797,6 +852,8 @@ casilda_compositor_toplevel_set_position (CasildaCompositorToplevel *toplevel, g
toplevel->y = y;
casilda_compositor_queue_draw (toplevel->priv);
compositor_update_adjustment_toplevel (toplevel->priv, toplevel);
}
static void
@ -1064,8 +1121,8 @@ on_motion_controller_enter (G_GNUC_UNUSED GtkEventControllerMotion *self,
gdouble y,
CasildaCompositorPrivate *priv)
{
priv->pointer_x = x;
priv->pointer_y = y;
priv->pointer_x = x + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
priv->pointer_y = y + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
casilda_compositor_handle_pointer_motion (priv);
wlr_seat_pointer_notify_frame (priv->seat);
}
@ -1084,8 +1141,12 @@ on_motion_controller_motion (G_GNUC_UNUSED GtkEventControllerMotion *self,
CasildaCompositorPrivate *priv)
{
/* Clamp pointer to widget coordinates */
priv->pointer_x = CLAMP (x, 0, gtk_widget_get_width (priv->self));
priv->pointer_y = CLAMP (y, 0, gtk_widget_get_height (priv->self));
x = CLAMP (x, 0, gtk_widget_get_width (priv->self));
y = CLAMP (y, 0, gtk_widget_get_height (priv->self));
/* Add scrollable offset */
priv->pointer_x = x + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
priv->pointer_y = y + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
casilda_compositor_handle_pointer_motion (priv);
wlr_seat_pointer_notify_frame (priv->seat);
@ -1646,6 +1707,8 @@ casilda_compositor_dispose (GObject *object)
g_clear_object (&priv->scroll_controller);
g_clear_object (&priv->key_controller);
g_clear_object (&priv->click_gesture);
g_clear_object (&priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
g_clear_object (&priv->adjustment[GTK_ORIENTATION_VERTICAL]);
casilda_compositor_reset_cursor (priv);
@ -1676,6 +1739,34 @@ casilda_compositor_finalize (GObject *object)
G_OBJECT_CLASS (casilda_compositor_parent_class)->finalize (object);
}
static void
compositor_adjustment_value_changed (G_GNUC_UNUSED GtkAdjustment *adjustment, gpointer data)
{
gtk_widget_queue_draw (GTK_WIDGET (data));
}
static void
compositor_set_adjustment (CasildaCompositor *compositor, GtkOrientation orientation, GtkAdjustment *adjustment)
{
CasildaCompositorPrivate *priv = GET_PRIVATE (compositor);
GtkAdjustment **adjustment_prop = &priv->adjustment[orientation];
if (adjustment && adjustment == *adjustment_prop)
return;
if (*adjustment_prop)
g_signal_handlers_disconnect_by_func (*adjustment_prop, compositor_adjustment_value_changed, compositor);
if (!adjustment)
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
g_set_object (adjustment_prop, g_object_ref_sink (adjustment));
g_signal_connect (adjustment, "value-changed", G_CALLBACK (compositor_adjustment_value_changed), compositor);
compositor_adjustment_value_changed (adjustment, compositor);
}
static void
casilda_compositor_set_property (GObject *object,
guint prop_id,
@ -1691,7 +1782,15 @@ casilda_compositor_set_property (GObject *object,
case PROP_SOCKET:
g_set_str (&priv->socket, g_value_get_string (value));
break;
case PROP_HADJUSTMENT:
compositor_set_adjustment (CASILDA_COMPOSITOR (object), GTK_ORIENTATION_HORIZONTAL, g_value_get_object (value));
break;
case PROP_VADJUSTMENT:
compositor_set_adjustment (CASILDA_COMPOSITOR (object), GTK_ORIENTATION_VERTICAL, g_value_get_object (value));
break;
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
break;
case PROP_BG_COLOR:
break;
@ -1717,7 +1816,16 @@ casilda_compositor_get_property (GObject *object,
case PROP_SOCKET:
g_value_set_string (value, priv->socket);
break;
case PROP_HADJUSTMENT:
g_value_set_object (value, priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
break;
case PROP_VADJUSTMENT:
g_value_set_object (value, priv->adjustment[GTK_ORIENTATION_VERTICAL]);
break;
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
g_value_set_enum (value, GTK_SCROLL_NATURAL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -1776,6 +1884,12 @@ casilda_compositor_class_init (CasildaCompositorClass *klass)
widget_class->realize = casilda_compositor_realize;
widget_class->unrealize = casilda_compositor_unrealize;
/* GtkScrollable implementation */
g_object_class_override_property (object_class, PROP_HADJUSTMENT, "hadjustment");
g_object_class_override_property (object_class, PROP_VADJUSTMENT, "vadjustment");
g_object_class_override_property (object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
g_object_class_override_property (object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
/* Properties */
/**
@ -1818,9 +1932,12 @@ static void
xdg_toplevel_map (struct wl_listener *listener, G_GNUC_UNUSED void *data)
{
CasildaCompositorToplevel *toplevel = wl_container_of (listener, toplevel, map);
CasildaCompositorPrivate *priv = toplevel->priv;
struct wlr_xdg_toplevel *xdg_toplevel = toplevel->xdg_toplevel;
CasildaCompositorToplevelState *state = toplevel->state;
struct wlr_box box;
gint offset_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
gint offset_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
wlr_xdg_surface_get_geometry (xdg_toplevel->base, &box);
@ -1835,17 +1952,17 @@ xdg_toplevel_map (struct wl_listener *listener, G_GNUC_UNUSED void *data)
wlr_xdg_surface_get_geometry (parent->xdg_toplevel->base, &parent_box);
/* Start in the middle of the parent window and cap to 0,0 */
toplevel->x = MAX(0, parent->x + (parent_box.width - box.width) / 2);
toplevel->y = MAX(0, parent->y + (parent_box.height - box.height) / 2);
toplevel->x = MAX(offset_x, parent->x + (parent_box.width - box.width) / 2);
toplevel->y = MAX(offset_y, parent->y + (parent_box.height - box.height) / 2);
parent->toplevels = g_list_prepend (parent->toplevels, toplevel);
}
}
else
{
/* Start in the middle of the compositor */
toplevel->x = MAX(0, (gtk_widget_get_width (toplevel->priv->self) - box.width) / 2);
toplevel->y = MAX(0, (gtk_widget_get_height (toplevel->priv->self) - box.height) / 2);
/* Start in the middle of the compositor viewport */
toplevel->x = offset_x + MAX(0, (gtk_widget_get_width (toplevel->priv->self) - box.width) / 2);
toplevel->y = offset_y + MAX(0, (gtk_widget_get_height (toplevel->priv->self) - box.height) / 2);
toplevel->priv->toplevels = g_list_prepend (toplevel->priv->toplevels, toplevel);
}
@ -2299,9 +2416,9 @@ on_gl_message_callback (G_GNUC_UNUSED GLenum source,
G_GNUC_UNUSED const void* userParam)
{
if (type == GL_DEBUG_TYPE_ERROR)
g_warning("GL: type = 0x%x, severity = 0x%x, message = %s\n", type, severity, message);
g_warning ("GL: type = 0x%x, severity = 0x%x, message = %s\n", type, severity, message);
else
g_message("GL: type = 0x%x, severity = 0x%x, message = %s\n", type, severity, message);
g_message ("GL: type = 0x%x, severity = 0x%x, message = %s\n", type, severity, message);
}
#endif