diff --git a/c/gtk3/bombs/bombs.c b/c/gtk3/bombs/bombs.c new file mode 100644 index 0000000..d103c7c --- /dev/null +++ b/c/gtk3/bombs/bombs.c @@ -0,0 +1,134 @@ +#include +#include + + +const double pi = 3.141592653589793; + +GtkWidget *window; +cairo_surface_t *surface; +cairo_t *context; + +gboolean dropping = FALSE; +double x = 0.0; +double y = 0.0; +double r = 0.0; +double speed = 0.0; +double mouse_x = 0.0; +double mouse_y = 0.0; + + +gboolean timeout(gpointer data) { + if (dropping) { + y += speed; + r += 1.0; + speed += 1.0; + + int iy = (int)y; + int ix = (int)x; + if ( ix < 0 || ix >= cairo_image_surface_get_width(surface) + || iy < 0 || iy >= cairo_image_surface_get_height(surface) ) + { + dropping = FALSE; + } else { + unsigned char *pixels = cairo_image_surface_get_data(surface); + int stright = cairo_image_surface_get_stride(surface); + unsigned char *pixel = pixels + iy*stright + ix*4; + if (pixel[3] > 0) { // alpha channel + dropping = FALSE; + cairo_save(context); + cairo_set_operator(context, CAIRO_OPERATOR_CLEAR); + cairo_arc(context, x, y, r, 0, 2*pi); + cairo_fill(context); + cairo_restore(context); + cairo_surface_flush(surface); + } + } + gtk_widget_queue_draw(window); + } + return TRUE; +} + +gboolean motion(GtkWidget *widget, GdkEventMotion *event, gpointer data) { + if (event->state & GDK_BUTTON1_MASK) { + cairo_save(context); + cairo_set_line_cap(context, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width(context, 50); + cairo_set_source_rgba(context, 0, 0, 0, 1); + cairo_move_to(context, mouse_x, mouse_y); + cairo_line_to(context, event->x, event->y); + cairo_stroke(context); + cairo_restore(context); + cairo_surface_flush(surface); + gtk_widget_queue_draw(window); + } + mouse_x = event->x; + mouse_y = event->y; + return TRUE; +} + +gboolean button_press(GtkWidget *widget, GdkEventButton *event, gpointer data) { + if (event->button == 2) { + cairo_save(context); + cairo_set_operator(context, CAIRO_OPERATOR_CLEAR); + cairo_paint(context); + cairo_restore(context); + cairo_surface_flush(surface); + gtk_widget_queue_draw(window); + } else + if (event->button == 3) { + x = event->x; + y = event->y; + r = 0; + speed = 0; + dropping = TRUE; + gtk_widget_queue_draw(window); + } + return TRUE; +} + +gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) { + cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 1); + cairo_paint(cr); + + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + if (dropping) { + cairo_set_line_width(cr, 1); + cairo_arc(cr, x, y, r, 0, 2*pi); + cairo_set_source_rgba(cr, 1, 1, 1, 1); + cairo_fill_preserve(cr); + cairo_set_source_rgba(cr, 0, 0, 0, 1); + cairo_stroke(cr); + } + return TRUE; +} + +void activate(GtkApplication* app, gpointer data) { + window = gtk_application_window_new (app); + g_signal_connect(window, "draw", G_CALLBACK(draw), NULL); + g_signal_connect(window, "motion-notify-event", G_CALLBACK(motion), NULL); + g_signal_connect(window, "button-press-event", G_CALLBACK(button_press), NULL); + gtk_widget_add_events(window, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK); + gtk_window_set_default_size( + GTK_WINDOW(window), + cairo_image_surface_get_width(surface), + cairo_image_surface_get_height(surface) ); + gtk_widget_show_all(window); + + g_timeout_add(10, timeout, NULL); +} + +int main(int argc, char **argv) { + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 700, 500); + context = cairo_create(surface); + + GtkApplication *application = gtk_application_new(NULL, 0); + g_signal_connect(application, "activate", G_CALLBACK(activate), NULL); + int status = g_application_run(G_APPLICATION(application), argc, argv); + g_object_unref(application); + + cairo_destroy(context); + cairo_surface_destroy(surface); + return status; +} +