|
/*! |
|
* \brief A simple animation using cairo and GTK+ |
|
* |
|
* This program shows high /usr/libexec/Xorg load while running full screen on Linux |
|
* |
|
* Compile with: |
|
* gcc `pkg-config --cflags --libs gtk+-3.0` -lm main.c |
|
*/ |
|
|
|
#include <gtk/gtk.h> |
|
#include <math.h> |
|
#include <cairo.h> |
|
|
|
#define NUM_WINDOWS (1u) |
|
#define NUM_POINTS (1000u) |
|
#define PERIOD (100u) |
|
|
|
/* Local function prototypes */ |
|
static gboolean invalidate_cb(void *); |
|
static gboolean drawing_area_draw_cb (GtkWidget *, cairo_t *, void *); |
|
|
|
int |
|
main (int argc, char **argv) |
|
{ |
|
unsigned int i; |
|
gtk_init (&argc, &argv); |
|
|
|
for (i = 0; i < NUM_WINDOWS; i++) |
|
{ |
|
GtkWidget *main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); |
|
gtk_window_set_title(GTK_WINDOW (main_window), "Drawing example"); |
|
gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 400); |
|
GtkWidget *drawing_area = gtk_drawing_area_new (); |
|
|
|
gtk_container_add (GTK_CONTAINER (main_window), drawing_area); |
|
gtk_widget_show_all (main_window); |
|
|
|
/* Create a timer to invalidate our window at 60Hz */ |
|
g_timeout_add (1000 / 60, invalidate_cb, drawing_area); |
|
|
|
/* Connect our redraw callback */ |
|
g_signal_connect (drawing_area, "draw", G_CALLBACK (drawing_area_draw_cb), NULL); |
|
|
|
/* Connect the destroy signal */ |
|
g_signal_connect (main_window, "destroy", G_CALLBACK (gtk_main_quit), NULL); |
|
} |
|
gtk_main (); |
|
} |
|
|
|
static gboolean |
|
invalidate_cb (void *ptr) |
|
{ |
|
if (GTK_IS_WIDGET(ptr)) |
|
{ |
|
gtk_widget_queue_draw (GTK_WIDGET (ptr)); |
|
return TRUE; |
|
} |
|
return FALSE; |
|
} |
|
|
|
static inline float |
|
sine_to_point (int x, int width, int height) |
|
{ |
|
(void) width; |
|
return (height / 2.0) * sin (x * 2 * M_PI / (PERIOD)) + height / 2.0; |
|
} |
|
|
|
static int current_width = -1; |
|
static int current_height = -1; |
|
static cairo_surface_t *current_source = NULL; |
|
static cairo_surface_t *current_temp = NULL; |
|
static double last_x = -1; |
|
static double last_y = -1; |
|
static int redraw_number = 0; |
|
|
|
static void |
|
update_drawing (int width, int height, cairo_surface_t *target) |
|
{ |
|
cairo_t *context; |
|
unsigned int i; |
|
|
|
if (width != current_width || height != current_height) |
|
{ |
|
cairo_surface_destroy (current_source); |
|
cairo_surface_destroy (current_temp); |
|
|
|
current_width = width; |
|
current_height = height; |
|
current_source = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR, NUM_POINTS, height); |
|
current_temp = cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR, NUM_POINTS, height); |
|
|
|
context = cairo_create (current_source); |
|
|
|
/* Redraw everything */ |
|
|
|
/* Draw the background */ |
|
cairo_set_source_rgb (context, 1, 1, 1); |
|
cairo_paint (context); |
|
|
|
/* Draw a moving sine wave */ |
|
cairo_set_source_rgb (context, 0.5, 0.5, 0); |
|
cairo_move_to (context, 0, sine_to_point (0 + redraw_number, width, height)); |
|
for (i = 1; i < NUM_POINTS; i++) |
|
{ |
|
cairo_line_to (context, i, sine_to_point (i + redraw_number, width, height)); |
|
} |
|
cairo_get_current_point (context, &last_x, &last_y); |
|
cairo_stroke (context); |
|
|
|
cairo_destroy (context); |
|
} |
|
else |
|
{ |
|
cairo_surface_t *temp; |
|
|
|
/* Move everything left and add a new data point */ |
|
context = cairo_create (current_temp); |
|
|
|
/* Scroll */ |
|
cairo_set_source_surface (context, current_source, -1, 0); |
|
cairo_paint (context); |
|
cairo_set_source_rgb (context, 1, 1, 1); |
|
cairo_rectangle (context, NUM_POINTS-1, 0, 1, height); |
|
cairo_fill (context); |
|
|
|
/* Add new point */ |
|
cairo_set_source_rgb (context, 0.5, 0.5, 0); |
|
cairo_move_to (context, last_x-1, last_y); |
|
cairo_line_to (context, NUM_POINTS-1, sine_to_point (NUM_POINTS + redraw_number, width, height)); |
|
cairo_get_current_point (context, &last_x, &last_y); |
|
cairo_stroke (context); |
|
|
|
cairo_destroy (context); |
|
|
|
/* Swap surfaces */ |
|
temp = current_temp; |
|
current_temp = current_source; |
|
current_source = temp; |
|
} |
|
} |
|
|
|
static gboolean |
|
drawing_area_draw_cb (GtkWidget *widget, cairo_t *context, void *ptr) |
|
{ |
|
int width, height; |
|
|
|
(void) ptr; |
|
|
|
width = gtk_widget_get_allocated_width (widget); |
|
height = gtk_widget_get_allocated_height (widget); |
|
update_drawing(width, height, cairo_get_target (context)); |
|
|
|
/* Draw the background */ |
|
cairo_set_source_rgb (context, 1, 1, 1); |
|
cairo_paint (context); |
|
|
|
/* Draw the content */ |
|
cairo_set_source_surface (context, current_source, 0, 0); |
|
cairo_paint (context); |
|
|
|
redraw_number++; |
|
return TRUE; |
|
} |
|
|
|
/* EOF */ |