1 #include <gtk/gtk.h>
2 
3 /* Surface to store current scribbles */
4 static cairo_surface_t *surface = NULL;
5 
6 static void
clear_surface(void)7 clear_surface (void)
8 {
9   cairo_t *cr;
10 
11   cr = cairo_create (surface);
12 
13   cairo_set_source_rgb (cr, 1, 1, 1);
14   cairo_paint (cr);
15 
16   cairo_destroy (cr);
17 }
18 
19 /* Create a new surface of the appropriate size to store our scribbles */
20 static void
resize_cb(GtkWidget * widget,int width,int height,gpointer data)21 resize_cb (GtkWidget *widget,
22            int        width,
23            int        height,
24            gpointer   data)
25 {
26   if (surface)
27     {
28       cairo_surface_destroy (surface);
29       surface = NULL;
30     }
31 
32   if (gtk_native_get_surface (gtk_widget_get_native (widget)))
33     {
34       surface = gdk_surface_create_similar_surface (gtk_native_get_surface (gtk_widget_get_native (widget)),
35                                                    CAIRO_CONTENT_COLOR,
36                                                    gtk_widget_get_width (widget),
37                                                    gtk_widget_get_height (widget));
38 
39       /* Initialize the surface to white */
40       clear_surface ();
41     }
42 }
43 
44 /* Redraw the screen from the surface. Note that the draw
45  * callback receives a ready-to-be-used cairo_t that is already
46  * clipped to only draw the exposed areas of the widget
47  */
48 static void
draw_cb(GtkDrawingArea * drawing_area,cairo_t * cr,int width,int height,gpointer data)49 draw_cb (GtkDrawingArea *drawing_area,
50          cairo_t        *cr,
51          int             width,
52          int             height,
53          gpointer        data)
54 {
55   cairo_set_source_surface (cr, surface, 0, 0);
56   cairo_paint (cr);
57 }
58 
59 /* Draw a rectangle on the surface at the given position */
60 static void
draw_brush(GtkWidget * widget,double x,double y)61 draw_brush (GtkWidget *widget,
62             double     x,
63             double     y)
64 {
65   cairo_t *cr;
66 
67   /* Paint to the surface, where we store our state */
68   cr = cairo_create (surface);
69 
70   cairo_rectangle (cr, x - 3, y - 3, 6, 6);
71   cairo_fill (cr);
72 
73   cairo_destroy (cr);
74 
75   /* Now invalidate the drawing area. */
76   gtk_widget_queue_draw (widget);
77 }
78 
79 static double start_x;
80 static double start_y;
81 
82 static void
drag_begin(GtkGestureDrag * gesture,double x,double y,GtkWidget * area)83 drag_begin (GtkGestureDrag *gesture,
84             double          x,
85             double          y,
86             GtkWidget      *area)
87 {
88   start_x = x;
89   start_y = y;
90 
91   draw_brush (area, x, y);
92 }
93 
94 static void
drag_update(GtkGestureDrag * gesture,double x,double y,GtkWidget * area)95 drag_update (GtkGestureDrag *gesture,
96              double          x,
97              double          y,
98              GtkWidget      *area)
99 {
100   draw_brush (area, start_x + x, start_y + y);
101 }
102 
103 static void
drag_end(GtkGestureDrag * gesture,double x,double y,GtkWidget * area)104 drag_end (GtkGestureDrag *gesture,
105           double          x,
106           double          y,
107           GtkWidget      *area)
108 {
109   draw_brush (area, start_x + x, start_y + y);
110 }
111 
112 static void
pressed(GtkGestureClick * gesture,int n_press,double x,double y,GtkWidget * area)113 pressed (GtkGestureClick *gesture,
114          int              n_press,
115          double           x,
116          double           y,
117          GtkWidget       *area)
118 {
119   clear_surface ();
120   gtk_widget_queue_draw (area);
121 }
122 
123 static void
close_window(void)124 close_window (void)
125 {
126   if (surface)
127     cairo_surface_destroy (surface);
128 }
129 
130 static void
activate(GtkApplication * app,gpointer user_data)131 activate (GtkApplication *app,
132           gpointer        user_data)
133 {
134   GtkWidget *window;
135   GtkWidget *drawing_area;
136   GtkGesture *drag;
137   GtkGesture *press;
138 
139   window = gtk_application_window_new (app);
140   gtk_window_set_title (GTK_WINDOW (window), "Drawing Area");
141 
142   g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
143 
144   drawing_area = gtk_drawing_area_new ();
145   /* set a minimum size */
146   gtk_widget_set_size_request (drawing_area, 100, 100);
147 
148   gtk_window_set_child (GTK_WINDOW (window), drawing_area);
149 
150   gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL);
151 
152   g_signal_connect_after (drawing_area, "resize", G_CALLBACK (resize_cb), NULL);
153 
154   drag = gtk_gesture_drag_new ();
155   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
156   gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (drag));
157   g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area);
158   g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area);
159   g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), drawing_area);
160 
161   press = gtk_gesture_click_new ();
162   gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY);
163   gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (press));
164 
165   g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area);
166 
167   gtk_widget_show (window);
168 }
169 
170 int
main(int argc,char ** argv)171 main (int    argc,
172       char **argv)
173 {
174   GtkApplication *app;
175   int status;
176 
177   app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
178   g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
179   status = g_application_run (G_APPLICATION (app), argc, argv);
180   g_object_unref (app);
181 
182   return status;
183 }
184