diff --git a/src/bling.c b/src/bling.c index 7135245e9be22d54c02874970763b4bbac81e49f..9aa453690ea07530c8da78df9e8233b69f3c6c37 100644 --- a/src/bling.c +++ b/src/bling.c @@ -37,6 +37,9 @@ phoc_bling_render (PhocBling *self, PhocRenderContext *ctx) g_assert (PHOC_IS_BLING (self)); + if (!phoc_bling_is_mapped (self)) + return; + iface = PHOC_BLING_GET_IFACE (self); g_assert (iface->render); diff --git a/src/cairo-texture.c b/src/cairo-texture.c index 55585d2b10a7672dca39db8280c02f91a2e68b60..9c7435c09e20e123bf475b48e06c936d866e0d4a 100644 --- a/src/cairo-texture.c +++ b/src/cairo-texture.c @@ -129,7 +129,7 @@ phoc_cairo_texture_constructed (GObject *object) self->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, self->width, self->height); surface_status = cairo_surface_status (self->surface); if (surface_status != CAIRO_STATUS_SUCCESS) { - g_warning ("Failed to create cairo surface: %d\n", surface_status); + g_warning ("Failed to create cairo surface: %d", surface_status); return; } self->cairo = cairo_create (self->surface); diff --git a/src/desktop.c b/src/desktop.c index 8dc79937c49078a5891ff0454c9ed468ac7dee86..6842c8a97dae1c467096b3ee6e177bddbbb96ae6 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -55,6 +55,7 @@ #include "wlr-layer-shell-unstable-v1-protocol.h" #include "layer-shell-effects.h" #include "workspace.h" +#include "workspace-indicator.h" #include "workspace-manager.h" #include "xdg-toplevel.h" #include "xdg-toplevel-decoration.h" @@ -830,16 +831,57 @@ workspace_damage_view_iter (PhocWorkspace *workspace, PhocView *view, gpointer u return TRUE; } +#define INDICATOR_OFFSET 16 +#define INDICATOR_SIZE 64 + +static void +show_workspace_indicator (PhocDesktop *self, int num) +{ + PhocOutput *output; + g_autoptr (PhocTimedAnimation) fade_anim = NULL; + g_autoptr (PhocPropertyEaser) easer = NULL; + g_autoptr (PhocColorRect) rect = NULL; + + if (!phoc_desktop_get_enable_animations (self)) + return; + + wl_list_for_each (output, &self->outputs, link) { + GSList *blings = phoc_output_get_blings (output); + + for (GSList *l = blings; l; l = l->next) { + if (PHOC_IS_WORKSPACE_INDICATOR (l->data)) { + phoc_output_remove_bling (output, l->data); + break; + } + } + } + + wl_list_for_each (output, &self->outputs, link) { + g_autoptr (PhocWorkspaceIndicator) indicator = NULL; + + indicator = phoc_workspace_indicator_new (PHOC_ANIMATABLE (output), + num, + output->lx + INDICATOR_OFFSET, + output->ly + INDICATOR_OFFSET, + INDICATOR_SIZE); + phoc_output_add_bling (output, PHOC_BLING (indicator)); + phoc_bling_map (PHOC_BLING (indicator)); + } +} + static void on_active_workspace_changed (PhocDesktop *self, GParamSpec *pspec, PhocWorkspaceManager *manager) { PhocDesktopPrivate *priv = phoc_desktop_get_instance_private (self); + int index; if (priv->active_workspace) phoc_workspace_for_each_view (priv->active_workspace, workspace_damage_view_iter, NULL); priv->active_workspace = phoc_workspace_manager_get_active (priv->workspace_manager); + index = phoc_workspace_manager_get_active_index (priv->workspace_manager); + show_workspace_indicator (self, index + 1); phoc_workspace_for_each_view (priv->active_workspace, workspace_damage_view_iter, NULL); } diff --git a/src/layer-shell.c b/src/layer-shell.c index 99e4c15dac35e69e5e902c5fc0618436ed83ab60..fbb0209a973b89ed6ce468f09a9239c8775de099 100644 --- a/src/layer-shell.c +++ b/src/layer-shell.c @@ -123,7 +123,7 @@ phoc_layer_shell_update_cursors (PhocLayerSurface *layer_surface) phoc_cursor_update_position (cursor, time.tv_sec * 1000 + time.tv_nsec / 1000000); } else { - g_critical ("Failed to get time, not updating position. Errno: %s\n", strerror (errno)); + g_critical ("Failed to get time, not updating position. Errno: %s", strerror (errno)); } } } diff --git a/src/meson.build b/src/meson.build index de1221e2b858ec605eca5e79513a718c26d079a5..123eb6328ef355ff74d41ead6ba14e6413bd20c9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -124,6 +124,8 @@ sources = files( 'view.h', 'virtual.c', 'virtual.h', + 'workspace-indicator.c', + 'workspace-indicator.h', 'workspace-manager.c', 'workspace-manager.h', 'workspace.c', diff --git a/src/output.c b/src/output.c index 7d324ec294331f511d5fac4a61cd4cc99e714203..6ebc5bbf44297c16033adaa2d4ae1a13e31176aa 100644 --- a/src/output.c +++ b/src/output.c @@ -56,7 +56,7 @@ typedef struct _PhocOutputPrivate { PhocRenderer *renderer; PhocOutputShield *shield; - GSList *frame_callbacks; + GSList *frame_callbacks; /* (element-type: PhocOutputFrameCallbackInfo) */ gint frame_callback_next_id; gint64 last_frame_us; @@ -80,7 +80,8 @@ typedef struct _PhocOutputPrivate { PhocLayoutTransaction *transaction; gboolean modeset_shield; - GSList *debug_damage; + GSList *blings; /* (element-type: PhocBling) */ + GSList *debug_damage; /* (element-type: PhocDebugDamageRegion) */ } PhocOutputPrivate; static void phoc_output_initable_iface_init (GInitableIface *iface); @@ -453,6 +454,7 @@ count_surface_iterator (PhocOutput *output, PHOC_TRACE_NO_INLINE static bool scan_out_fullscreen_view (PhocOutput *self, PhocView *view, struct wlr_output_state *pending) { + PhocOutputPrivate *priv = phoc_output_get_instance_private (self); PhocServer *server = phoc_server_get_default (); PhocInput *input = phoc_server_get_input (server); PhocDesktop *desktop = phoc_server_get_desktop (server); @@ -479,6 +481,9 @@ scan_out_fullscreen_view (PhocOutput *self, PhocView *view, struct wlr_output_st if (phoc_output_has_shell_revealed (self)) return false; + if (priv->blings) + return false; + if (phoc_output_has_layer (self, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)) return false; @@ -2527,3 +2532,67 @@ phoc_output_get_debug_damage (PhocOutput *self) return priv->debug_damage; } + +/** + * phoc_output_add_bling: + * @self: The output + * @bling: The bling to add + * + * By adding a [type@Bling] to an output you ensure that it gets rendered + * just before the output if the output is active and the bling is mapped. + * + * The output will take a reference on the [type@Bling] that will be + * dropped when the bling is removed or the output is destroyed. + */ +void +phoc_output_add_bling (PhocOutput *self, PhocBling *bling) +{ + PhocOutputPrivate *priv; + + g_assert (PHOC_IS_OUTPUT (self)); + g_assert (PHOC_IS_BLING (bling)); + priv = phoc_output_get_instance_private (self); + + priv->blings = g_slist_prepend (priv->blings, g_object_ref (bling)); +} + +/** + * phoc_output_remove_bling: + * @self: The output + * @bling: The bling to remove + * + * Removes the given bling from the output. + */ +void +phoc_output_remove_bling (PhocOutput *self, PhocBling *bling) +{ + PhocOutputPrivate *priv; + + g_assert (PHOC_IS_OUTPUT (self)); + g_assert (PHOC_IS_BLING (bling)); + priv = phoc_output_get_instance_private (self); + + g_return_if_fail (g_slist_find (priv->blings, bling)); + + priv->blings = g_slist_remove (priv->blings, bling); + g_object_unref (bling); +} + +/** + * phoc_output_get_blings: + * @self: The output + * + * Gets the output's current list of blings. + * + * Returns: (transfer none)(element-type PhocBling): A list + */ +GSList * +phoc_output_get_blings (PhocOutput *self) +{ + PhocOutputPrivate *priv; + + g_assert (PHOC_IS_OUTPUT (self)); + priv = phoc_output_get_instance_private (self); + + return priv->blings; +} diff --git a/src/output.h b/src/output.h index 630d887753b774370a9940dbe54abefc3171db64..06d346994d5b5e3a04798b3a8789c8a064e44f5b 100644 --- a/src/output.h +++ b/src/output.h @@ -191,4 +191,8 @@ GSList *phoc_output_get_debug_damage (PhocOutput *self); enum wlr_scale_filter_mode phoc_output_get_texture_filter_mode (PhocOutput *self); +void phoc_output_add_bling (PhocOutput *self, PhocBling *bling); +void phoc_output_remove_bling (PhocOutput *self, PhocBling *bling); +GSList * phoc_output_get_blings (PhocOutput *self); + G_END_DECLS diff --git a/src/render.c b/src/render.c index ee72ca2d9d7e1e60301d44ac398fad7764c9577c..18f723a6434de61155adb68bcc0beae51c7a6655 100644 --- a/src/render.c +++ b/src/render.c @@ -269,6 +269,23 @@ render_layer (enum zwlr_layer_shell_v1_layer layer, PhocRenderContext *ctx) } +static void +render_output_blings (PhocOutput *output, PhocRenderContext *ctx) +{ + GSList *blings; + + blings = phoc_output_get_blings (output); + if (!blings) + return; + + for (GSList *l = blings; l; l = l->next) { + PhocBling *bling = PHOC_BLING (l->data); + + phoc_bling_render (bling, ctx); + } +} + + static void render_drag_icons (PhocInput *input, PhocRenderContext *ctx) { @@ -520,6 +537,8 @@ phoc_renderer_render_output (PhocRenderer *self, PhocOutput *output, PhocRenderC render_layer (ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ctx); + render_output_blings (output, ctx); + renderer_end: wlr_output_add_software_cursors_to_render_pass (wlr_output, ctx->render_pass, damage); diff --git a/src/spinner.c b/src/spinner.c index ab94aed1d4557420591f4ed85021c0d71eeea789..95ebdd3b8503c4277093b2386de4ea8e533639b9 100644 --- a/src/spinner.c +++ b/src/spinner.c @@ -258,7 +258,7 @@ bling_map (PhocBling *bling) cr = phoc_cairo_texture_get_context (self->texture); if (!cr) { - g_warning ("No Cairo context, cannot render spinner\n"); + g_warning ("No Cairo context, cannot render spinner"); return; } diff --git a/src/workspace-indicator.c b/src/workspace-indicator.c new file mode 100644 index 0000000000000000000000000000000000000000..39153bcc31ece8bedb6a6259dff228e212806918 --- /dev/null +++ b/src/workspace-indicator.c @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2025 Phosh.mobi e.V. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "phoc-workspace-indicator" + +#include "phoc-config.h" + +#include "bling.h" +#include "cairo-texture.h" +#include "server.h" +#include "timed-animation.h" +#include "workspace-indicator.h" + +#include +#include + +#define FADE_DURATION_MS 200 +#define SHOW_DURATION_MS 200 +#define FONT "Sans Bold 32" + +/** + * PhocWorkspaceIndicator: + * + * The workspace indicator + */ + +enum { + PROP_0, + PROP_NUM, + PROP_LX, + PROP_LY, + PROP_SIZE, + PROP_ANIMATABLE, + PROP_ALPHA, + PROP_LAST_PROP +}; +static GParamSpec *props[PROP_LAST_PROP]; + +struct _PhocWorkspaceIndicator { + GObject parent; + + int lx; + int ly; + + PhocAnimatable *animatable; + PhocTimedAnimation *animation; + PhocPropertyEaser *easer; + float alpha; + int num; + int size; + guint timeout_id; + PhocCairoTexture *texture; +}; + +static void bling_interface_init (PhocBlingInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (PhocWorkspaceIndicator, phoc_workspace_indicator, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (PHOC_TYPE_BLING, bling_interface_init)) + + +static void +on_show_timeout (gpointer data) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (data); + + g_assert (PHOC_IS_WORKSPACE_INDICATOR (data)); + + self->timeout_id = 0; + phoc_bling_unmap (PHOC_BLING (self)); +} + + +static void +on_animation_done (PhocWorkspaceIndicator *self) +{ + self->timeout_id = g_timeout_add_once (SHOW_DURATION_MS, on_show_timeout, self); +} + + +static PhocBox +bling_get_box (PhocBling *bling) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (bling); + + return (PhocBox) { + .x = self->lx, + .y = self->ly, + .width = self->size, + .height = self->size, + }; +} + + +static void +bling_render (PhocBling *bling, PhocRenderContext *ctx) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (bling); + struct wlr_render_texture_options options; + struct wlr_box box = bling_get_box (bling); + pixman_region32_t damage; + struct wlr_texture *texture = phoc_cairo_texture_get_texture (self->texture); + + if (!texture) + return; + + box.x -= ctx->output->lx; + box.y -= ctx->output->ly; + phoc_utils_scale_box (&box, ctx->output->wlr_output->scale); + phoc_output_transform_box (ctx->output, &box); + + if (!phoc_utils_is_damaged (&box, ctx->damage, NULL, &damage)) { + pixman_region32_fini (&damage); + return; + } + + options = (struct wlr_render_texture_options) { + .alpha = &self->alpha, + .texture = texture, + .dst_box = box, + .clip = &damage, + .transform = ctx->output->wlr_output->transform, + }; + + wlr_render_pass_add_texture (ctx->render_pass, &options); +} + + +static void +draw_indicator (cairo_t *cr, const char *text, double size) +{ + g_autoptr (PangoLayout) layout = NULL; + g_autoptr (PangoFontDescription) desc = NULL; + int layout_width, layout_height; + double offset_x, offset_y, radius; + const double degrees = M_PI / 180.0; + + cairo_save (cr); + + radius = size / 10.0; + cairo_new_sub_path (cr); + cairo_arc (cr, size - radius, radius, radius, -90 * degrees, 0 * degrees); + cairo_arc (cr, size - radius, size - radius, radius, 0 * degrees, 90 * degrees); + cairo_arc (cr, radius, size - radius, radius, 90 * degrees, 180 * degrees); + cairo_arc (cr, radius, radius, radius, 180 * degrees, 270 * degrees); + cairo_close_path (cr); + + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8); + cairo_fill_preserve (cr); + + cairo_restore (cr); + + layout = pango_cairo_create_layout (cr); + + pango_layout_set_text (layout, text, -1); + desc = pango_font_description_from_string (FONT); + pango_layout_set_font_description (layout, desc); + + cairo_save (cr); + + cairo_set_source_rgb (cr, 0.8, 0.8, 0.8); + + pango_cairo_update_layout (cr, layout); + + pango_layout_get_size (layout, &layout_width, &layout_height); + + offset_x = (size - (double)layout_width / PANGO_SCALE) / 2.0; + offset_y = (size - (double)layout_height / PANGO_SCALE) / 2.0; + cairo_move_to (cr, offset_x, offset_y); + pango_cairo_show_layout (cr, layout); + + cairo_restore (cr); +} + + +static void +bling_map (PhocBling *bling) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (bling); + PhocServer *server = phoc_server_get_default (); + PhocDesktop *desktop = phoc_server_get_desktop (server); + PhocOutput *output = phoc_desktop_layout_get_output (desktop, self->lx, self->ly); + g_autofree char *text = NULL; + float size = self->size; + cairo_t *cr; + + if (output) + size = ceil (size * phoc_output_get_scale (output)); + + if (self->texture) + return; + + self->texture = phoc_cairo_texture_new (size, size); + cr = phoc_cairo_texture_get_context (self->texture); + + if (!cr) { + g_warning ("No Cairo context, cannot render workspace indicator"); + return; + } + + cairo_set_antialias (cr, CAIRO_ANTIALIAS_FAST); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + + text = g_strdup_printf ("%d", self->num); + draw_indicator (cr, text, self->size); + + phoc_cairo_texture_update (self->texture); + + phoc_bling_damage_box (PHOC_BLING (self)); + phoc_timed_animation_play (self->animation); +} + + +static void +bling_unmap (PhocBling *bling) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (bling); + + if (!self->texture) + return; + + phoc_bling_damage_box (PHOC_BLING (self)); + phoc_timed_animation_reset (self->animation); + + g_clear_object (&self->texture); +} + + +static gboolean +bling_is_mapped (PhocBling *bling) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (bling); + + return self->texture != NULL; +} + + +static void +bling_interface_init (PhocBlingInterface *iface) +{ + iface->get_box = bling_get_box; + iface->render = bling_render; + iface->map = bling_map; + iface->unmap = bling_unmap; + iface->is_mapped = bling_is_mapped; +} + + +static void +phoc_workspace_indicator_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (object); + + switch (property_id) { + case PROP_NUM: + self->num = g_value_get_int (value); + break; + case PROP_LX: + phoc_bling_damage_box (PHOC_BLING (self)); + self->lx = g_value_get_int (value); + phoc_bling_damage_box (PHOC_BLING (self)); + break; + case PROP_LY: + phoc_bling_damage_box (PHOC_BLING (self)); + self->ly = g_value_get_int (value); + phoc_bling_damage_box (PHOC_BLING (self)); + break; + case PROP_SIZE: + self->size = g_value_get_int (value); + break; + case PROP_ANIMATABLE: + self->animatable = g_value_get_object (value); + break; + case PROP_ALPHA: + self->alpha = g_value_get_float (value); + phoc_bling_damage_box (PHOC_BLING (self)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +phoc_workspace_indicator_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (object); + + switch (property_id) { + case PROP_NUM: + g_value_set_int (value, self->num); + break; + case PROP_LX: + g_value_set_int (value, self->lx); + break; + case PROP_LY: + g_value_set_int (value, self->ly); + break; + case PROP_SIZE: + g_value_set_int (value, self->size); + break; + case PROP_ANIMATABLE: + g_value_set_object (value, self->animatable); + break; + case PROP_ALPHA: + g_value_set_float (value, self->alpha); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +phoc_workspace_indicator_constructed (GObject *object) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (object); + + G_OBJECT_CLASS (phoc_workspace_indicator_parent_class)->constructed (object); + + self->animation = g_object_new (PHOC_TYPE_TIMED_ANIMATION, + "animatable", g_steal_pointer (&self->animatable), + "duration", FADE_DURATION_MS, + "property-easer", self->easer, + NULL); + g_signal_connect_swapped (self->animation, "done", + G_CALLBACK (on_animation_done), + self); +} + + +static void +phoc_workspace_indicator_finalize (GObject *object) +{ + PhocWorkspaceIndicator *self = PHOC_WORKSPACE_INDICATOR (object); + + g_clear_handle_id (&self->timeout_id, g_source_remove); + + phoc_bling_unmap (PHOC_BLING (self)); + g_clear_object (&self->easer); + g_clear_object (&self->animation); + + G_OBJECT_CLASS (phoc_workspace_indicator_parent_class)->finalize (object); +} + + +static void +phoc_workspace_indicator_class_init (PhocWorkspaceIndicatorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = phoc_workspace_indicator_get_property; + object_class->set_property = phoc_workspace_indicator_set_property; + object_class->constructed = phoc_workspace_indicator_constructed; + object_class->finalize = phoc_workspace_indicator_finalize; + + /** + * PhocWorkspaceIndicator:animatable: + * + * A [type@Animatable] implementation that can drive this workspace indicator's animation + */ + props[PROP_ANIMATABLE] = + g_param_spec_object ("animatable", "", "", + PHOC_TYPE_ANIMATABLE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + /** + * PhocWorkspaceIndicator:num: + * + * The workspace number + */ + props[PROP_NUM] = + g_param_spec_int ("num", "", "", + 0, INT32_MAX, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PhocWorkspaceIndicator:lx: + * + * The x coord to render workspace indicator at + */ + props[PROP_LX] = + g_param_spec_int ("lx", "", "", + 0, INT32_MAX, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PhocWorkspaceIndicator:ly: + * + * The y coord to render workspace indicator at + */ + props[PROP_LY] = + g_param_spec_int ("ly", "", "", + 0, INT32_MAX, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PhocWorkspaceIndicator:alpha: + * + * The current alpha of the workspace indicator. + */ + props[PROP_ALPHA] = + g_param_spec_float ("alpha", "", "", + 0, 1.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * PhocWorkspaceIndicator:size: + * + * The width and height of the workspace indicator. + */ + props[PROP_SIZE] = + g_param_spec_int ("size", "", "", + 16, 256, 128, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + + +static void +phoc_workspace_indicator_init (PhocWorkspaceIndicator *self) +{ + self->easer = g_object_new (PHOC_TYPE_PROPERTY_EASER, + "target", self, + "easing", PHOC_EASING_NONE, + NULL); + phoc_property_easer_set_props (self->easer, + "alpha", 0.0, 1.0, + NULL); +} + + +PhocWorkspaceIndicator * +phoc_workspace_indicator_new (PhocAnimatable *animatable, int num, int lx, int ly, int size) +{ + return g_object_new (PHOC_TYPE_WORKSPACE_INDICATOR, + "animatable", animatable, + "num", num, + "lx", lx, + "ly", ly, + "size", size, + NULL); +} diff --git a/src/workspace-indicator.h b/src/workspace-indicator.h new file mode 100644 index 0000000000000000000000000000000000000000..c8f37a2d4f1d2bd3ba39a5a1d4a60e09226c14f6 --- /dev/null +++ b/src/workspace-indicator.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2025 Phosh.mobi e.V. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define PHOC_TYPE_WORKSPACE_INDICATOR (phoc_workspace_indicator_get_type ()) + +G_DECLARE_FINAL_TYPE (PhocWorkspaceIndicator, phoc_workspace_indicator, PHOC, WORKSPACE_INDICATOR, + GObject) + + +PhocWorkspaceIndicator *phoc_workspace_indicator_new (PhocAnimatable *animatable, + int num, + int lx, + int ly, + int size); + +G_END_DECLS