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
14static GtkWidget *demo_window = NULL;
15static GtkWidget *shadertoy = NULL;
16static GtkTextBuffer *textbuffer = NULL;
17
18static void
19run (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
31static void
32run_clicked_cb (GtkWidget *button,
33 gpointer user_data)
34{
35 run ();
36}
37
38static void
39load_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
52static void
53clear_clicked_cb (GtkWidget *button,
54 gpointer user_data)
55{
56 gtk_text_buffer_set_text (buffer: textbuffer, text: "", len: 0);
57}
58
59static void
60close_window (GtkWidget *widget)
61{
62 /* Reset the state */
63 demo_window = NULL;
64 shadertoy = NULL;
65 textbuffer = NULL;
66}
67
68static GtkWidget *
69new_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
83static GtkWidget *
84new_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
98static GtkWidget *
99create_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
190GtkWidget *
191do_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

source code of gtk/demos/gtk-demo/shadertoy.c