mirror of
https://github.com/brl/mutter.git
synced 2024-11-10 07:56:14 -05:00
2f445682b1
The current "create context/draw/destroy context" pattern presents various problems. The first issue is that it defers memory management to the caller of the create() or create_region() methods, which makes bookkeeping of the cairo_t* harder for language bindings and third party libraries. The second issue is that, while it's easier for draw-and-forget texturs, this API is needlessly complicated for contents that have to change programmatically - and it introduces constraints like calling the drawing code explicitly after a surface resize (e.g. inside an allocate() implementation). By using a signal-based approach we can make the CairoTexture actor behave like other actors, and like other libraries using Cairo as their 2D drawing API. The semantics of the newly-introduced ::draw signal are the same as the one used by GTK+: - the signal is emitted on invalidation; - the cairo_t* context is owned by the actor; - it is safe to have multiple callbacks attached to the same signal, to allow composition; - the cairo_t* is already clipped to the invalidated area, so that Cairo can discard geometry immediately before we upload the texture data. There are possible future improvements, like coalescing multiple invalidations inside regions, and performing clipped draws during the paint cycle; we could even perform clipped redraws if we know the extent of the invalidated area.
108 lines
3.1 KiB
C
108 lines
3.1 KiB
C
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <cairo.h>
|
|
#include <clutter/clutter.h>
|
|
|
|
static gboolean
|
|
draw_clock (ClutterCairoTexture *canvas,
|
|
cairo_t *cr)
|
|
{
|
|
guint width, height;
|
|
GDateTime *now;
|
|
float hours, minutes, seconds;
|
|
|
|
/* get the current time and compute the angles */
|
|
now = g_date_time_new_now_local ();
|
|
seconds = g_date_time_get_second (now) * G_PI / 30;
|
|
minutes = g_date_time_get_minute (now) * G_PI / 30;
|
|
hours = g_date_time_get_hour (now) * G_PI / 6;
|
|
|
|
/* clear the contents of the canvas, to avoid painting
|
|
* over the previous frame
|
|
*/
|
|
clutter_cairo_texture_clear (canvas);
|
|
|
|
/* scale the modelview to the size of the surface */
|
|
clutter_cairo_texture_get_surface_size (canvas, &width, &height);
|
|
cairo_scale (cr, width, height);
|
|
|
|
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
|
|
cairo_set_line_width (cr, 0.1);
|
|
|
|
/* the black rail that holds the seconds indicator */
|
|
clutter_cairo_set_source_color (cr, CLUTTER_COLOR_Black);
|
|
cairo_translate (cr, 0.5, 0.5);
|
|
cairo_arc (cr, 0, 0, 0.4, 0, G_PI * 2);
|
|
cairo_stroke (cr);
|
|
|
|
/* the seconds indicator */
|
|
clutter_cairo_set_source_color (cr, CLUTTER_COLOR_White);
|
|
cairo_move_to (cr, 0, 0);
|
|
cairo_arc (cr, sinf (seconds) * 0.4, - cosf (seconds) * 0.4, 0.05, 0, G_PI * 2);
|
|
cairo_fill (cr);
|
|
|
|
/* the minutes hand */
|
|
clutter_cairo_set_source_color (cr, CLUTTER_COLOR_DarkChameleon);
|
|
cairo_set_source_rgba (cr, 0.2, 0.2, 1, 0.6);
|
|
cairo_move_to (cr, 0, 0);
|
|
cairo_line_to (cr, sinf (minutes) * 0.4, -cosf (minutes) * 0.4);
|
|
cairo_stroke (cr);
|
|
|
|
/* the hours hand */
|
|
cairo_move_to (cr, 0, 0);
|
|
cairo_line_to (cr, sinf (hours) * 0.2, -cosf (hours) * 0.2);
|
|
cairo_stroke (cr);
|
|
|
|
g_date_time_unref (now);
|
|
|
|
/* we're done drawing */
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
invalidate_clock (gpointer data_)
|
|
{
|
|
/* invalidate the contents of the canvas */
|
|
clutter_cairo_texture_invalidate (data_);
|
|
|
|
/* keep the timeout source */
|
|
return TRUE;
|
|
}
|
|
|
|
G_MODULE_EXPORT int
|
|
test_cairo_clock_main (int argc, char *argv[])
|
|
{
|
|
ClutterActor *stage, *canvas;
|
|
|
|
/* initialize Clutter */
|
|
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
|
|
return EXIT_FAILURE;
|
|
|
|
/* create a fixed size stage */
|
|
stage = clutter_stage_new ();
|
|
clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock");
|
|
clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_LightSkyBlue);
|
|
clutter_actor_set_size (stage, 300, 300);
|
|
clutter_actor_show (stage);
|
|
|
|
/* our 2D canvas, courtesy of Cairo */
|
|
canvas = clutter_cairo_texture_new (300, 300);
|
|
clutter_container_add_actor (CLUTTER_CONTAINER (stage), canvas);
|
|
|
|
/* quit on destroy */
|
|
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
|
|
|
/* connect our drawing code */
|
|
g_signal_connect (canvas, "draw", G_CALLBACK (draw_clock), NULL);
|
|
|
|
/* invalidate the canvas, so that we can draw before the main loop starts */
|
|
clutter_cairo_texture_invalidate (CLUTTER_CAIRO_TEXTURE (canvas));
|
|
|
|
/* set up a timer that invalidates the canvas every second */
|
|
clutter_threads_add_timeout (1000, invalidate_clock, canvas);
|
|
|
|
clutter_main ();
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|