1 | /* OpenGL/Shadertoy |
2 | * #Keywords: GtkGLArea |
3 | * |
4 | * Generate pixels using a custom fragment shader. |
5 | * |
6 | * The names of the uniforms are compatible with the shaders on shadertoy.com, so |
7 | * many of the shaders there work here too. |
8 | */ |
9 | #include <math.h> |
10 | #include <gtk/gtk.h> |
11 | #include <epoxy/gl.h> |
12 | #include "gtkshadertoy.h" |
13 | |
14 | static GtkWidget *demo_window = NULL; |
15 | static GtkWidget *shadertoy = NULL; |
16 | static GtkTextBuffer *textbuffer = NULL; |
17 | |
18 | static void |
19 | run (void) |
20 | { |
21 | GtkTextIter start, end; |
22 | char *text; |
23 | |
24 | gtk_text_buffer_get_bounds (buffer: textbuffer, start: &start, end: &end); |
25 | text = gtk_text_buffer_get_text (buffer: textbuffer, start: &start, end: &end, FALSE); |
26 | |
27 | gtk_shadertoy_set_image_shader (GTK_SHADERTOY (shadertoy), shader: text); |
28 | g_free (mem: text); |
29 | } |
30 | |
31 | static void |
32 | run_clicked_cb (GtkWidget *button, |
33 | gpointer user_data) |
34 | { |
35 | run (); |
36 | } |
37 | |
38 | static void |
39 | load_clicked_cb (GtkWidget *button, |
40 | gpointer user_data) |
41 | { |
42 | const char *path = user_data; |
43 | GBytes *initial_shader; |
44 | |
45 | initial_shader = g_resources_lookup_data (path, lookup_flags: 0, NULL); |
46 | gtk_text_buffer_set_text (buffer: textbuffer, text: g_bytes_get_data (bytes: initial_shader, NULL), len: -1); |
47 | g_bytes_unref (bytes: initial_shader); |
48 | |
49 | run (); |
50 | } |
51 | |
52 | static void |
53 | clear_clicked_cb (GtkWidget *button, |
54 | gpointer user_data) |
55 | { |
56 | gtk_text_buffer_set_text (buffer: textbuffer, text: "" , len: 0); |
57 | } |
58 | |
59 | static void |
60 | close_window (GtkWidget *widget) |
61 | { |
62 | /* Reset the state */ |
63 | demo_window = NULL; |
64 | shadertoy = NULL; |
65 | textbuffer = NULL; |
66 | } |
67 | |
68 | static GtkWidget * |
69 | new_shadertoy (const char *path) |
70 | { |
71 | GBytes *shader; |
72 | GtkWidget *toy; |
73 | |
74 | toy = gtk_shadertoy_new (); |
75 | shader = g_resources_lookup_data (path, lookup_flags: 0, NULL); |
76 | gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy), |
77 | shader: g_bytes_get_data (bytes: shader, NULL)); |
78 | g_bytes_unref (bytes: shader); |
79 | |
80 | return toy; |
81 | } |
82 | |
83 | static GtkWidget * |
84 | new_button (const char *path) |
85 | { |
86 | GtkWidget *button, *toy; |
87 | |
88 | button = gtk_button_new (); |
89 | g_signal_connect (button, "clicked" , G_CALLBACK (load_clicked_cb), (char *)path); |
90 | |
91 | toy = new_shadertoy (path); |
92 | gtk_widget_set_size_request (widget: toy, width: 64, height: 36); |
93 | gtk_button_set_child (GTK_BUTTON (button), child: toy); |
94 | |
95 | return button; |
96 | } |
97 | |
98 | static GtkWidget * |
99 | create_shadertoy_window (GtkWidget *do_widget) |
100 | { |
101 | GtkWidget *window, *box, *hbox, *button, *textview, *sw, *aspect, *centerbox; |
102 | |
103 | window = gtk_window_new (); |
104 | gtk_window_set_display (GTK_WINDOW (window), display: gtk_widget_get_display (widget: do_widget)); |
105 | gtk_window_set_title (GTK_WINDOW (window), title: "Shadertoy" ); |
106 | gtk_window_set_default_size (GTK_WINDOW (window), width: 690, height: 740); |
107 | g_signal_connect (window, "destroy" , G_CALLBACK (close_window), NULL); |
108 | |
109 | box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, FALSE); |
110 | gtk_widget_set_margin_start (widget: box, margin: 12); |
111 | gtk_widget_set_margin_end (widget: box, margin: 12); |
112 | gtk_widget_set_margin_top (widget: box, margin: 12); |
113 | gtk_widget_set_margin_bottom (widget: box, margin: 12); |
114 | gtk_box_set_spacing (GTK_BOX (box), spacing: 6); |
115 | gtk_window_set_child (GTK_WINDOW (window), child: box); |
116 | |
117 | aspect = gtk_aspect_frame_new (xalign: 0.5, yalign: 0.5, ratio: 1.77777, FALSE); |
118 | gtk_widget_set_hexpand (widget: aspect, TRUE); |
119 | gtk_widget_set_vexpand (widget: aspect, TRUE); |
120 | gtk_box_append (GTK_BOX (box), child: aspect); |
121 | |
122 | shadertoy = new_shadertoy (path: "/shadertoy/alienplanet.glsl" ); |
123 | gtk_aspect_frame_set_child (GTK_ASPECT_FRAME (aspect), child: shadertoy); |
124 | |
125 | sw = gtk_scrolled_window_new (); |
126 | gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), height: 250); |
127 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
128 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
129 | hscrollbar_policy: GTK_POLICY_AUTOMATIC, |
130 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
131 | gtk_widget_set_hexpand (widget: sw, TRUE); |
132 | gtk_box_append (GTK_BOX (box), child: sw); |
133 | |
134 | textview = gtk_text_view_new (); |
135 | gtk_text_view_set_monospace (GTK_TEXT_VIEW (textview), TRUE); |
136 | g_object_set (object: textview, |
137 | first_property_name: "left-margin" , 20, |
138 | "right-margin" , 20, |
139 | "top-margin" , 20, |
140 | "bottom-margin" , 20, |
141 | NULL); |
142 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: textview); |
143 | |
144 | textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); |
145 | gtk_text_buffer_set_text (buffer: textbuffer, |
146 | text: gtk_shadertoy_get_image_shader (GTK_SHADERTOY (shadertoy)), |
147 | len: -1); |
148 | |
149 | centerbox = gtk_center_box_new (); |
150 | gtk_box_append (GTK_BOX (box), child: centerbox); |
151 | |
152 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, FALSE); |
153 | gtk_box_set_spacing (GTK_BOX (hbox), spacing: 6); |
154 | gtk_center_box_set_start_widget (GTK_CENTER_BOX (centerbox), child: hbox); |
155 | |
156 | button = gtk_button_new_from_icon_name (icon_name: "view-refresh-symbolic" ); |
157 | gtk_widget_set_tooltip_text (widget: button, text: "Restart the demo" ); |
158 | gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER); |
159 | g_signal_connect (button, "clicked" , G_CALLBACK (run_clicked_cb), NULL); |
160 | gtk_box_append (GTK_BOX (hbox), child: button); |
161 | |
162 | button = gtk_button_new_from_icon_name (icon_name: "edit-clear-all-symbolic" ); |
163 | gtk_widget_set_tooltip_text (widget: button, text: "Clear the text view" ); |
164 | gtk_widget_set_valign (widget: button, align: GTK_ALIGN_CENTER); |
165 | g_signal_connect (button, "clicked" , G_CALLBACK (clear_clicked_cb), NULL); |
166 | gtk_box_append (GTK_BOX (hbox), child: button); |
167 | |
168 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, FALSE); |
169 | gtk_box_set_spacing (GTK_BOX (hbox), spacing: 6); |
170 | gtk_center_box_set_end_widget (GTK_CENTER_BOX (centerbox), child: hbox); |
171 | |
172 | button = new_button (path: "/shadertoy/alienplanet.glsl" ); |
173 | gtk_box_append (GTK_BOX (hbox), child: button); |
174 | |
175 | button = new_button (path: "/shadertoy/mandelbrot.glsl" ); |
176 | gtk_box_append (GTK_BOX (hbox), child: button); |
177 | |
178 | button = new_button (path: "/shadertoy/neon.glsl" ); |
179 | gtk_box_append (GTK_BOX (hbox), child: button); |
180 | |
181 | button = new_button (path: "/shadertoy/cogs.glsl" ); |
182 | gtk_box_append (GTK_BOX (hbox), child: button); |
183 | |
184 | button = new_button (path: "/shadertoy/glowingstars.glsl" ); |
185 | gtk_box_append (GTK_BOX (hbox), child: button); |
186 | |
187 | return window; |
188 | } |
189 | |
190 | GtkWidget * |
191 | do_shadertoy (GtkWidget *do_widget) |
192 | { |
193 | if (!demo_window) |
194 | demo_window = create_shadertoy_window (do_widget); |
195 | |
196 | if (!gtk_widget_get_visible (widget: demo_window)) |
197 | gtk_widget_show (widget: demo_window); |
198 | else |
199 | gtk_window_destroy (GTK_WINDOW (demo_window)); |
200 | |
201 | return demo_window; |
202 | } |
203 | |