203 lines
5.9 KiB
C
203 lines
5.9 KiB
C
/* OpenGL/Shadertoy
|
|
* #Keywords: GtkGLArea
|
|
*
|
|
* Generate pixels using a custom fragment shader.
|
|
*
|
|
* The names of the uniforms are compatible with the shaders on shadertoy.com, so
|
|
* many of the shaders there work here too.
|
|
*/
|
|
#include <math.h>
|
|
#include <gtk/gtk.h>
|
|
#include <epoxy/gl.h>
|
|
#include "gtkshadertoy.h"
|
|
|
|
static GtkWidget *demo_window = NULL;
|
|
static GtkWidget *shadertoy = NULL;
|
|
static GtkTextBuffer *textbuffer = NULL;
|
|
|
|
static void
|
|
run (void)
|
|
{
|
|
GtkTextIter start, end;
|
|
char *text;
|
|
|
|
gtk_text_buffer_get_bounds (textbuffer, &start, &end);
|
|
text = gtk_text_buffer_get_text (textbuffer, &start, &end, FALSE);
|
|
|
|
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (shadertoy), text);
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
run_clicked_cb (GtkWidget *button,
|
|
gpointer user_data)
|
|
{
|
|
run ();
|
|
}
|
|
|
|
static void
|
|
load_clicked_cb (GtkWidget *button,
|
|
gpointer user_data)
|
|
{
|
|
const char *path = user_data;
|
|
GBytes *initial_shader;
|
|
|
|
initial_shader = g_resources_lookup_data (path, 0, NULL);
|
|
gtk_text_buffer_set_text (textbuffer, g_bytes_get_data (initial_shader, NULL), -1);
|
|
g_bytes_unref (initial_shader);
|
|
|
|
run ();
|
|
}
|
|
|
|
static void
|
|
clear_clicked_cb (GtkWidget *button,
|
|
gpointer user_data)
|
|
{
|
|
gtk_text_buffer_set_text (textbuffer, "", 0);
|
|
}
|
|
|
|
static void
|
|
close_window (GtkWidget *widget)
|
|
{
|
|
/* Reset the state */
|
|
demo_window = NULL;
|
|
shadertoy = NULL;
|
|
textbuffer = NULL;
|
|
}
|
|
|
|
static GtkWidget *
|
|
new_shadertoy (const char *path)
|
|
{
|
|
GBytes *shader;
|
|
GtkWidget *toy;
|
|
|
|
toy = gtk_shadertoy_new ();
|
|
shader = g_resources_lookup_data (path, 0, NULL);
|
|
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy),
|
|
g_bytes_get_data (shader, NULL));
|
|
g_bytes_unref (shader);
|
|
|
|
return toy;
|
|
}
|
|
|
|
static GtkWidget *
|
|
new_button (const char *path)
|
|
{
|
|
GtkWidget *button, *toy;
|
|
|
|
button = gtk_button_new ();
|
|
g_signal_connect (button, "clicked", G_CALLBACK (load_clicked_cb), (char *)path);
|
|
|
|
toy = new_shadertoy (path);
|
|
gtk_widget_set_size_request (toy, 64, 36);
|
|
gtk_button_set_child (GTK_BUTTON (button), toy);
|
|
|
|
return button;
|
|
}
|
|
|
|
static GtkWidget *
|
|
create_shadertoy_window (GtkWidget *do_widget)
|
|
{
|
|
GtkWidget *window, *box, *hbox, *button, *textview, *sw, *aspect, *centerbox;
|
|
|
|
window = gtk_window_new ();
|
|
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
|
|
gtk_window_set_title (GTK_WINDOW (window), "Shadertoy");
|
|
gtk_window_set_default_size (GTK_WINDOW (window), 690, 740);
|
|
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
|
|
gtk_widget_set_margin_start (box, 12);
|
|
gtk_widget_set_margin_end (box, 12);
|
|
gtk_widget_set_margin_top (box, 12);
|
|
gtk_widget_set_margin_bottom (box, 12);
|
|
gtk_box_set_spacing (GTK_BOX (box), 6);
|
|
gtk_window_set_child (GTK_WINDOW (window), box);
|
|
|
|
aspect = gtk_aspect_frame_new (0.5, 0.5, 1.77777, FALSE);
|
|
gtk_widget_set_hexpand (aspect, TRUE);
|
|
gtk_widget_set_vexpand (aspect, TRUE);
|
|
gtk_box_append (GTK_BOX (box), aspect);
|
|
|
|
shadertoy = new_shadertoy ("/shadertoy/alienplanet.glsl");
|
|
gtk_aspect_frame_set_child (GTK_ASPECT_FRAME (aspect), shadertoy);
|
|
|
|
sw = gtk_scrolled_window_new ();
|
|
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 250);
|
|
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
gtk_widget_set_hexpand (sw, TRUE);
|
|
gtk_box_append (GTK_BOX (box), sw);
|
|
|
|
textview = gtk_text_view_new ();
|
|
gtk_text_view_set_monospace (GTK_TEXT_VIEW (textview), TRUE);
|
|
g_object_set (textview,
|
|
"left-margin", 20,
|
|
"right-margin", 20,
|
|
"top-margin", 20,
|
|
"bottom-margin", 20,
|
|
NULL);
|
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), textview);
|
|
|
|
textbuffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
|
|
gtk_text_buffer_set_text (textbuffer,
|
|
gtk_shadertoy_get_image_shader (GTK_SHADERTOY (shadertoy)),
|
|
-1);
|
|
|
|
centerbox = gtk_center_box_new ();
|
|
gtk_box_append (GTK_BOX (box), centerbox);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
|
gtk_box_set_spacing (GTK_BOX (hbox), 6);
|
|
gtk_center_box_set_start_widget (GTK_CENTER_BOX (centerbox), hbox);
|
|
|
|
button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
|
|
gtk_widget_set_tooltip_text (button, "Restart the demo");
|
|
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
|
|
g_signal_connect (button, "clicked", G_CALLBACK (run_clicked_cb), NULL);
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
button = gtk_button_new_from_icon_name ("edit-clear-all-symbolic");
|
|
gtk_widget_set_tooltip_text (button, "Clear the text view");
|
|
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
|
|
g_signal_connect (button, "clicked", G_CALLBACK (clear_clicked_cb), NULL);
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE);
|
|
gtk_box_set_spacing (GTK_BOX (hbox), 6);
|
|
gtk_center_box_set_end_widget (GTK_CENTER_BOX (centerbox), hbox);
|
|
|
|
button = new_button ("/shadertoy/alienplanet.glsl");
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
button = new_button ("/shadertoy/mandelbrot.glsl");
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
button = new_button ("/shadertoy/neon.glsl");
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
button = new_button ("/shadertoy/cogs.glsl");
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
button = new_button ("/shadertoy/glowingstars.glsl");
|
|
gtk_box_append (GTK_BOX (hbox), button);
|
|
|
|
return window;
|
|
}
|
|
|
|
GtkWidget *
|
|
do_shadertoy (GtkWidget *do_widget)
|
|
{
|
|
if (!demo_window)
|
|
demo_window = create_shadertoy_window (do_widget);
|
|
|
|
if (!gtk_widget_get_visible (demo_window))
|
|
gtk_widget_set_visible (demo_window, TRUE);
|
|
else
|
|
gtk_window_destroy (GTK_WINDOW (demo_window));
|
|
|
|
return demo_window;
|
|
}
|