summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2019-06-17 23:21:28 +0200
committerThomas White <taw@physics.org>2019-06-17 23:21:28 +0200
commit40130e129b523283e77dc7546e3a32671e0e1541 (patch)
tree6ceba5dcbce5e2a477dee8a658382f022ef1fcf6
parent4eaf7dc0119d3367dfdf4290200944a45d10c1e4 (diff)
Move UI stuff to a separate file
-rw-r--r--meson.build1
-rw-r--r--src/display.c655
-rw-r--r--src/display.h28
-rw-r--r--src/nanolight.c616
4 files changed, 686 insertions, 614 deletions
diff --git a/meson.build b/meson.build
index e24ba6e..d75170b 100644
--- a/meson.build
+++ b/meson.build
@@ -23,6 +23,7 @@ executable('nanolight',
['src/nanolight.c',
'src/command.c',
'src/scanout.c',
+ 'src/display.c',
],
dependencies : [gtk_dep, mdep, soup_dep],
install : true)
diff --git a/src/display.c b/src/display.c
new file mode 100644
index 0000000..72dfb2b
--- /dev/null
+++ b/src/display.c
@@ -0,0 +1,655 @@
+/*
+ * display.c
+ *
+ * Copyright © 2019 Thomas White <taw@bitwiz.me.uk>
+ *
+ * This file is part of NanoLight.
+ *
+ * NanoLight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include <gtk/gtk.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <libintl.h>
+#define _(x) gettext(x)
+
+#include "nanolight.h"
+#include "command.h"
+
+#define OVERALL_BORDER (20.0)
+#define OVERALL_SPLIT (0.5)
+#define FIXTURE_BORDER (5.0)
+
+enum attr_class key_attrs[] = {
+ 0,
+ ATT_INTENSITY, /* F1 */
+ ATT_TILT, /* F2 */
+ ATT_STROBE, /* F3 */
+ ATT_CYAN, /* F4 */
+ ATT_MAGENTA, /* F5 */
+ ATT_YELLOW, /* F6 */
+ ATT_GOBO, /* F7 */
+ ATT_RGOBO, /* F8 */
+ ATT_PRISM, /* F9 */
+ ATT_FOCUS, /* F10 */
+ ATT_ZOOM, /* F11 */
+ ATT_ZOOM, /* F12 */
+};
+
+static double get_attr_val(struct fixture *fix, enum attr_class acls)
+{
+ int i;
+ for ( i=0; i<fix->cls->n_attrs; i++ ) {
+ if ( fix->cls->attrs[i].cls == acls ) {
+ int max = 255;
+ if ( fix->cls->attrs[i].props & ATTR_16BIT ) max = 65535;
+ return (double)fix->attr_vals[i] / max;
+ }
+ }
+ return 0.0;
+}
+
+
+static int fixture_selected(struct nanolight *nl, struct fixture *fix)
+{
+ int i;
+ for ( i=0; i<nl->n_sel; i++ ) {
+ if ( &nl->fixtures[nl->selection[i]] == fix ) return 1;
+ }
+ return 0;
+}
+
+
+static int find_attribute(struct fixture *fix, enum attr_class cls, int *n)
+{
+ int j;
+ for ( j=0; j<fix->cls->n_attrs; j++ ) {
+ if ( fix->cls->attrs[j].cls == cls ) {
+ *n = j;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static void draw_fixture(cairo_t *cr, PangoContext *pc, PangoFontDescription *fontdesc,
+ struct nanolight *nl, struct fixture *fix)
+{
+ PangoLayout *layout;
+ const double w = 40.0;
+ const double h = 3.0/2.0*w;
+ char tmp[32];
+ int n;
+
+ /* Pan/tilt (underneath rectangle) */
+ if ( find_attribute(fix, ATT_PAN, &n) ) {
+ double x = w*fix->attr_vals[n] / 65535;
+ cairo_move_to(cr, x, -1.0);
+ cairo_line_to(cr, x, h+1.0);
+ cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
+ cairo_set_line_width(cr, 1.0);
+ cairo_stroke(cr);
+ }
+ if ( find_attribute(fix, ATT_TILT, &n) ) {
+ double y = h*(1.0 - (double)fix->attr_vals[n] / 65535);
+ cairo_move_to(cr, -1.0, y);
+ cairo_line_to(cr, w+1.0, y);
+ cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
+ cairo_set_line_width(cr, 1.0);
+ cairo_stroke(cr);
+ }
+
+ cairo_rectangle(cr, 0.0, 0.0, w, h);
+ if ( fixture_selected(nl, fix) ) {
+ cairo_set_source_rgba(cr, 0.3, 0.3, 0.9, 0.9);
+ } else {
+ cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 0.9);
+ }
+ cairo_fill_preserve(cr);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_set_line_width(cr, 1.0);
+ cairo_stroke(cr);
+
+ /* Label */
+ layout = pango_layout_new(pc);
+ pango_layout_set_text(layout, fix->label, -1);
+ pango_layout_set_width(layout, (w*PANGO_SCALE)-4.0);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_font_description(layout, fontdesc);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_move_to(cr, 0.0, 2.0);
+ pango_cairo_show_layout(cr, layout);
+ g_object_unref(layout);
+
+ /* Intensity */
+ snprintf(tmp, 32, "%.0f %%", get_attr_val(fix, ATT_INTENSITY)*100.0);
+ layout = pango_layout_new(pc);
+ pango_layout_set_text(layout, tmp, -1);
+ pango_layout_set_width(layout, (w*PANGO_SCALE)-4.0);
+ pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+ pango_layout_set_font_description(layout, fontdesc);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_move_to(cr, 0.0, 15.0);
+ pango_cairo_show_layout(cr, layout);
+ g_object_unref(layout);
+}
+
+
+static const char *attr_text(enum attr_class cls)
+{
+ switch ( cls ) {
+ case ATT_INTENSITY : return "Intensity";
+ case ATT_PAN : return "(pan)";
+ case ATT_TILT : return "Pan/tilt";
+ case ATT_STROBE : return "Strobe";
+ case ATT_CYAN : return "Cyan";
+ case ATT_MAGENTA : return "Magenta";
+ case ATT_YELLOW : return "Yellow";
+ case ATT_RGOBO : return "RGobo";
+ case ATT_GOBO : return "Gobo";
+ case ATT_PRISM : return "Prism";
+ case ATT_FOCUS : return "Focus";
+ case ATT_ZOOM : return "Zoom";
+ }
+ return "(unknown)";
+}
+
+
+static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct nanolight *nl)
+{
+ int w, h;
+ int ch;
+ int i;
+ PangoContext *pc;
+ PangoFontDescription *fontdesc;
+ double x, y;
+ PangoRectangle cursor;
+
+ w = gtk_widget_get_allocated_width(widget);
+ h = gtk_widget_get_allocated_height(widget);
+ pc = gtk_widget_get_pango_context(widget);
+
+ /* Overall background */
+ cairo_set_source_rgb(cr, 0.0, 0.0, 0.2);
+ cairo_paint(cr);
+
+ /* Separator between fixture and cue areas */
+ cairo_move_to(cr, w*OVERALL_SPLIT, OVERALL_BORDER);
+ cairo_line_to(cr, w*OVERALL_SPLIT, h - OVERALL_BORDER);
+ cairo_set_line_width(cr, 3.0);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_stroke(cr);
+
+ /* Fixtures */
+ cairo_save(cr);
+ cairo_translate(cr, OVERALL_BORDER, OVERALL_BORDER);
+ x = FIXTURE_BORDER;
+ y = FIXTURE_BORDER;
+ fontdesc = pango_font_description_from_string("Comfortaa Bold 8");
+ for ( i=0; i<nl->n_fixtures; i++ ) {
+ cairo_save(cr);
+ cairo_translate(cr, x, y);
+ cairo_scale(cr, nl->fixture_width/40.0, nl->fixture_width/40.0);
+ draw_fixture(cr, pc, fontdesc, nl, &nl->fixtures[i]);
+ cairo_restore(cr);
+ x += nl->fixture_width + FIXTURE_BORDER*2;
+ if ( x + nl->fixture_width + FIXTURE_BORDER*2 > w*OVERALL_SPLIT ) {
+ x = FIXTURE_BORDER;
+ y += nl->fixture_width*3.0/2.0 + FIXTURE_BORDER*2;
+ }
+ }
+ cairo_restore(cr);
+
+ /* Command line */
+ pango_layout_set_text(nl->layout, nl->cmdline, -1);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
+ cairo_save(cr);
+ cairo_translate(cr, OVERALL_BORDER, h - OVERALL_BORDER - 20.0);
+ cairo_move_to(cr, 0.0, 0.0);
+ pango_cairo_show_layout(cr, nl->layout);
+ pango_layout_get_cursor_pos(nl->layout, nl->cursor_idx, &cursor, NULL);
+ x = pango_units_to_double(cursor.x);
+ y = pango_units_to_double(cursor.y);
+ ch = pango_units_to_double(cursor.height);
+ cairo_move_to(cr, x, y);
+ cairo_line_to(cr, x, y+ch);
+ cairo_set_line_width(cr, 3.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_source_rgb(cr, 0.8, 0.4, 0.4);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ /* Selected attribute indicator */
+ if ( nl->n_sel > 0 ) {
+ pango_layout_set_text(nl->sa_layout, attr_text(nl->sel_attr), -1);
+ cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
+ pango_layout_set_width(nl->sa_layout, pango_units_from_double(200.0));
+ cairo_move_to(cr, w - 200.0 - OVERALL_BORDER, h-OVERALL_BORDER-20.0);
+ pango_cairo_show_layout(cr, nl->sa_layout);
+ }
+
+ return FALSE;
+}
+
+
+static void redraw(struct nanolight *nl)
+{
+ gint w, h;
+ w = gtk_widget_get_allocated_width(GTK_WIDGET(nl->da));
+ h = gtk_widget_get_allocated_height(GTK_WIDGET(nl->da));
+ gtk_widget_queue_draw_area(GTK_WIDGET(nl->da), 0, 0, w, h);
+}
+
+
+static gboolean im_commit_sig(GtkIMContext *im, gchar *str, struct nanolight *nl)
+{
+ size_t cmd_len = strlen(nl->cmdline);
+ if ( cmd_len+strlen(str) > 1023 ) return FALSE;
+ strcat(nl->cmdline, str);
+ nl->cursor_idx += strlen(str);
+ redraw(nl);
+ return FALSE;
+}
+
+
+static size_t delete_char(char *str)
+{
+ char *last;
+ size_t len;
+ if ( str[0] == '\0' ) return 0;
+ last = g_utf8_find_prev_char(str, str+strlen(str));
+ len = strlen(last);
+ last[0] = '\0';
+ return len;
+}
+
+
+static void cap_value(struct fixture *fix, int n, signed int *v)
+{
+ if ( *v < 0 ) *v = 0;
+ if ( fix->cls->attrs[n].props & ATTR_16BIT ) {
+ if ( *v > 65535 ) *v = 65535;
+ } else {
+ if ( *v > 255 ) *v = 255;
+ }
+}
+
+
+static void set_start_attrs(struct nanolight *nl, enum attr_class cls)
+{
+ int i;
+ for ( i=0; i<nl->n_sel; i++ ) {
+ int n;
+ struct fixture *fix = &nl->fixtures[nl->selection[i]];
+ if ( find_attribute(fix, cls, &n) ) {
+ fix->attr_vals_start[n] = fix->attr_vals[n];
+ }
+ }
+
+ /* If altering tilt, also change pan */
+ if ( cls == ATT_TILT ) {
+ set_start_attrs(nl, ATT_PAN);
+ }
+}
+
+
+static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, struct nanolight *nl)
+{
+#if 0
+ GdkSeat *seat;
+ GdkWindow *win = gtk_widget_get_window(nl->da);
+
+ seat = gdk_display_get_default_seat(gdk_display_get_default());
+ nl->pointer = gdk_seat_get_pointer(seat);
+#endif
+
+ set_start_attrs(nl, nl->sel_attr);
+
+ nl->x_orig = event->x;
+ nl->y_orig = event->y;
+ nl->dragging = 1;
+
+ return FALSE;
+}
+
+
+static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, struct nanolight *nl)
+{
+ nl->dragging = 0;
+ return FALSE;
+}
+
+
+static double maybe_fine(struct fixture *fix, int n, double inc, int shift)
+{
+ if ( !(fix->cls->attrs[n].props & ATTR_16BIT) ) return inc;
+ if ( shift ) return inc;
+ return inc * 100.0;
+}
+
+
+static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, struct nanolight *nl)
+{
+ int i;
+ double x_inc, y_inc;
+ int shift;
+
+ if ( !nl->dragging ) return FALSE;
+
+ x_inc = (event->x - nl->x_orig)/3;
+ y_inc = (nl->y_orig - event->y)/3; /* Mouse up means increase */
+
+ shift = event->state & GDK_SHIFT_MASK;
+ if ( shift != nl->fine ) {
+ nl->fine = shift;
+ set_start_attrs(nl, nl->sel_attr);
+ nl->x_orig = event->x;
+ nl->y_orig = event->y;
+ return FALSE;
+ }
+
+ if ( nl->sel_attr == ATT_TILT ) {
+ for ( i=0; i<nl->n_sel; i++ ) {
+ int n;
+ struct fixture *fix = &nl->fixtures[nl->selection[i]];
+ if ( find_attribute(fix, ATT_PAN, &n) ) {
+ double inc = maybe_fine(fix, n, x_inc,
+ event->state & GDK_SHIFT_MASK);
+ signed int nv = fix->attr_vals_start[n] + inc;
+ cap_value(fix, n, &nv);
+ fix->attr_vals[n] = nv;
+ }
+ if ( find_attribute(fix, ATT_TILT, &n) ) {
+ double inc = maybe_fine(fix, n, y_inc,
+ event->state & GDK_SHIFT_MASK);
+ signed int nv = fix->attr_vals_start[n] + inc;
+ cap_value(fix, n, &nv);
+ fix->attr_vals[n] = nv;
+ }
+ }
+ } else {
+ for ( i=0; i<nl->n_sel; i++ ) {
+ int n;
+ struct fixture *fix = &nl->fixtures[nl->selection[i]];
+ if ( find_attribute(fix, nl->sel_attr, &n) ) {
+ double inc = maybe_fine(fix, n, y_inc,
+ event->state & GDK_SHIFT_MASK);
+ signed int nv = fix->attr_vals_start[n] + inc;
+ cap_value(fix, n, &nv);
+ if ( !(fix->cls->attrs[n].props & ATTR_STOP) ) {
+ fix->attr_vals[n] = nv;
+ } else {
+ printf("Can't change step attr with mouse\n");
+ }
+ }
+ }
+ }
+
+ redraw(nl);
+ return FALSE;
+}
+
+
+static void change_stop_attr(struct nanolight *nl, signed int inc)
+{
+ int i;
+ for ( i=0; i<nl->n_sel; i++ ) {
+
+ struct fixture *fix = &nl->fixtures[nl->selection[i]];
+ int n;
+
+ if ( find_attribute(fix, nl->sel_attr, &n) ) {
+ signed int nv;
+ if ( !(fix->cls->attrs[n].props & ATTR_STOP) ) {
+ printf("Can't change continuous attr with keys\n");
+ continue;
+ }
+ nv = fix->attr_vals[n] + inc;
+ if ( (nv>=0) && (nv<fix->cls->attrs[n].n_stops) ) {
+ fix->attr_vals[n] = nv;
+ }
+ }
+
+ }
+ redraw(nl);
+}
+
+static void home_value(struct nanolight *nl)
+{
+ int i;
+ for ( i=0; i<nl->n_sel; i++ ) {
+
+ struct fixture *fix = &nl->fixtures[nl->selection[i]];
+ int n;
+
+ if ( nl->sel_attr == ATT_TILT ) {
+ if ( find_attribute(fix, ATT_PAN, &n) ) {
+ fix->attr_vals[n] = fix->cls->attrs[n].home;
+ }
+ if ( find_attribute(fix, ATT_TILT, &n) ) {
+ fix->attr_vals[n] = fix->cls->attrs[n].home;
+ }
+ nl->dragging = 0;
+ } else {
+ if ( find_attribute(fix, nl->sel_attr, &n) ) {
+ fix->attr_vals[n] = fix->cls->attrs[n].home;
+ }
+ }
+
+ }
+ redraw(nl);
+}
+
+
+static void execute_command(struct nanolight *nl)
+{
+ if ( command_run(nl->cmdline, nl) == 0 ) {
+ nl->cmdline[0] = '\0';
+ nl->cursor_idx = 0;
+ }
+ redraw(nl);
+}
+
+
+static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, struct nanolight *nl)
+{
+ gboolean r;
+ int claim = 1;
+
+ switch ( event->keyval ) {
+
+ case GDK_KEY_Left :
+ break;
+
+ case GDK_KEY_Right :
+ break;
+
+ case GDK_KEY_Up :
+ change_stop_attr(nl, +1);
+ break;
+
+ case GDK_KEY_Down :
+ change_stop_attr(nl, -1);
+ break;
+
+ case GDK_KEY_Return :
+ execute_command(nl);
+ break;
+
+ case GDK_KEY_Escape :
+ nl->cmdline[0] = '\0';
+ nl->cursor_idx = 0;
+ nl->n_sel = 0;
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_BackSpace :
+ nl->cursor_idx -= delete_char(nl->cmdline);
+ break;
+
+ case GDK_KEY_KP_Enter :
+ printf("Go!\n");
+ break;
+
+ case GDK_KEY_KP_Add :
+ printf("Stop/back!\n");
+ break;
+
+ case GDK_KEY_Home :
+ home_value(nl);
+ break;
+
+ case GDK_KEY_F1 :
+ nl->sel_attr = key_attrs[1];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F2 :
+ nl->sel_attr = key_attrs[2];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F3 :
+ nl->sel_attr = key_attrs[3];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F4 :
+ nl->sel_attr = key_attrs[4];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F5 :
+ nl->sel_attr = key_attrs[5];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F6 :
+ nl->sel_attr = key_attrs[6];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F7 :
+ nl->sel_attr = key_attrs[7];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F8 :
+ nl->sel_attr = key_attrs[8];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F9 :
+ nl->sel_attr = key_attrs[9];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F10 :
+ nl->sel_attr = key_attrs[10];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F11 :
+ nl->sel_attr = key_attrs[11];
+ nl->dragging = 0;
+ break;
+
+ case GDK_KEY_F12 :
+ nl->sel_attr = key_attrs[12];
+ nl->dragging = 0;
+ break;
+
+ default :
+ claim = 0;
+ break;
+
+ }
+
+ if ( !claim ) {
+ /* Throw the event to the IM context and let it sort things out */
+ r = gtk_im_context_filter_keypress(GTK_IM_CONTEXT(nl->im_context), event);
+ if ( r ) claim = 1;
+ } else {
+ redraw(nl);
+ }
+
+ if ( claim ) return TRUE;
+ return FALSE;
+}
+
+
+static gint realise_sig(GtkWidget *da, struct nanolight *nl)
+{
+ GdkWindow *win = gtk_widget_get_window(da);
+ PangoContext *pc;
+ PangoFontDescription *fontdesc;
+
+ /* Keyboard and input method stuff */
+ nl->im_context = gtk_im_multicontext_new();
+ gtk_im_context_set_client_window(GTK_IM_CONTEXT(nl->im_context), win);
+ gdk_window_set_accept_focus(win, TRUE);
+ g_signal_connect(G_OBJECT(nl->im_context), "commit", G_CALLBACK(im_commit_sig), nl);
+ g_signal_connect(G_OBJECT(da), "key-press-event", G_CALLBACK(key_press_sig), nl);
+ g_signal_connect(G_OBJECT(da), "button-press-event", G_CALLBACK(button_press_sig), nl);
+ g_signal_connect(G_OBJECT(da), "button-release-event", G_CALLBACK(button_release_sig), nl);
+ g_signal_connect(G_OBJECT(da), "motion-notify-event", G_CALLBACK(motion_sig), nl);
+
+ pc = gtk_widget_get_pango_context(da);
+ fontdesc = pango_font_description_from_string("Comfortaa Bold 16");
+
+ nl->layout = pango_layout_new(pc);
+ pango_layout_set_alignment(nl->layout, PANGO_ALIGN_LEFT);
+ pango_layout_set_font_description(nl->layout, fontdesc);
+
+ nl->sa_layout = pango_layout_new(pc);
+ pango_layout_set_alignment(nl->sa_layout, PANGO_ALIGN_RIGHT);
+ pango_layout_set_font_description(nl->sa_layout, fontdesc);
+
+ return FALSE;
+}
+
+
+void create_main_window(struct nanolight *nl)
+{
+ GtkWidget *mainwindow;
+ GtkWidget *da;
+
+ /* Create main window */
+ mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_fullscreen(GTK_WINDOW(mainwindow));
+ g_signal_connect_swapped(G_OBJECT(mainwindow), "destroy", gtk_main_quit, NULL);
+
+ da = gtk_drawing_area_new();
+ nl->da = da;
+ gtk_container_add(GTK_CONTAINER(mainwindow), GTK_WIDGET(da));
+ gtk_widget_set_can_focus(GTK_WIDGET(da), TRUE);
+ gtk_widget_add_events(da, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
+ | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_BUTTON_MOTION_MASK);
+ g_signal_connect(G_OBJECT(da), "draw", G_CALLBACK(draw_sig), nl);
+ g_signal_connect(G_OBJECT(da), "realize", G_CALLBACK(realise_sig), nl);
+
+ gtk_widget_grab_focus(GTK_WIDGET(da));
+ gtk_widget_show_all(mainwindow);
+}
diff --git a/src/display.h b/src/display.h
new file mode 100644
index 0000000..eb4cb3e
--- /dev/null
+++ b/src/display.h
@@ -0,0 +1,28 @@
+/*
+ * scanout.h
+ *
+ * Copyright © 2019 Thomas White <taw@bitwiz.me.uk>
+ *
+ * This file is part of NanoLight.
+ *
+ * NanoLight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+extern void create_main_window(struct nanolight *nl);
+
+#endif /* DISPLAY_H */
diff --git a/src/nanolight.c b/src/nanolight.c
index 80bf874..aea925c 100644
--- a/src/nanolight.c
+++ b/src/nanolight.c
@@ -33,8 +33,8 @@
#define _(x) gettext(x)
#include "nanolight.h"
-#include "command.h"
#include "scanout.h"
+#include "display.h"
static void show_help(const char *s)
{
@@ -43,223 +43,6 @@ static void show_help(const char *s)
" -h, --help Display this help message.\n"));
}
-#define OVERALL_BORDER (20.0)
-#define OVERALL_SPLIT (0.5)
-#define FIXTURE_BORDER (5.0)
-
-enum attr_class key_attrs[] = {
- 0,
- ATT_INTENSITY, /* F1 */
- ATT_TILT, /* F2 */
- ATT_STROBE, /* F3 */
- ATT_CYAN, /* F4 */
- ATT_MAGENTA, /* F5 */
- ATT_YELLOW, /* F6 */
- ATT_GOBO, /* F7 */
- ATT_RGOBO, /* F8 */
- ATT_PRISM, /* F9 */
- ATT_FOCUS, /* F10 */
- ATT_ZOOM, /* F11 */
- ATT_ZOOM, /* F12 */
-};
-
-static double get_attr_val(struct fixture *fix, enum attr_class acls)
-{
- int i;
- for ( i=0; i<fix->cls->n_attrs; i++ ) {
- if ( fix->cls->attrs[i].cls == acls ) {
- int max = 255;
- if ( fix->cls->attrs[i].props & ATTR_16BIT ) max = 65535;
- return (double)fix->attr_vals[i] / max;
- }
- }
- return 0.0;
-}
-
-
-static int fixture_selected(struct nanolight *nl, struct fixture *fix)
-{
- int i;
- for ( i=0; i<nl->n_sel; i++ ) {
- if ( &nl->fixtures[nl->selection[i]] == fix ) return 1;
- }
- return 0;
-}
-
-
-static int find_attribute(struct fixture *fix, enum attr_class cls, int *n)
-{
- int j;
- for ( j=0; j<fix->cls->n_attrs; j++ ) {
- if ( fix->cls->attrs[j].cls == cls ) {
- *n = j;
- return 1;
- }
- }
- return 0;
-}
-
-
-static void draw_fixture(cairo_t *cr, PangoContext *pc, PangoFontDescription *fontdesc,
- struct nanolight *nl, struct fixture *fix)
-{
- PangoLayout *layout;
- const double w = 40.0;
- const double h = 3.0/2.0*w;
- char tmp[32];
- int n;
-
- /* Pan/tilt (underneath rectangle) */
- if ( find_attribute(fix, ATT_PAN, &n) ) {
- double x = w*fix->attr_vals[n] / 65535;
- cairo_move_to(cr, x, -1.0);
- cairo_line_to(cr, x, h+1.0);
- cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
- cairo_set_line_width(cr, 1.0);
- cairo_stroke(cr);
- }
- if ( find_attribute(fix, ATT_TILT, &n) ) {
- double y = h*(1.0 - (double)fix->attr_vals[n] / 65535);
- cairo_move_to(cr, -1.0, y);
- cairo_line_to(cr, w+1.0, y);
- cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
- cairo_set_line_width(cr, 1.0);
- cairo_stroke(cr);
- }
-
- cairo_rectangle(cr, 0.0, 0.0, w, h);
- if ( fixture_selected(nl, fix) ) {
- cairo_set_source_rgba(cr, 0.3, 0.3, 0.9, 0.9);
- } else {
- cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 0.9);
- }
- cairo_fill_preserve(cr);
- cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
- cairo_set_line_width(cr, 1.0);
- cairo_stroke(cr);
-
- /* Label */
- layout = pango_layout_new(pc);
- pango_layout_set_text(layout, fix->label, -1);
- pango_layout_set_width(layout, (w*PANGO_SCALE)-4.0);
- pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
- pango_layout_set_font_description(layout, fontdesc);
- cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
- cairo_move_to(cr, 0.0, 2.0);
- pango_cairo_show_layout(cr, layout);
- g_object_unref(layout);
-
- /* Intensity */
- snprintf(tmp, 32, "%.0f %%", get_attr_val(fix, ATT_INTENSITY)*100.0);
- layout = pango_layout_new(pc);
- pango_layout_set_text(layout, tmp, -1);
- pango_layout_set_width(layout, (w*PANGO_SCALE)-4.0);
- pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
- pango_layout_set_font_description(layout, fontdesc);
- cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
- cairo_move_to(cr, 0.0, 15.0);
- pango_cairo_show_layout(cr, layout);
- g_object_unref(layout);
-}
-
-
-static const char *attr_text(enum attr_class cls)
-{
- switch ( cls ) {
- case ATT_INTENSITY : return "Intensity";
- case ATT_PAN : return "(pan)";
- case ATT_TILT : return "Pan/tilt";
- case ATT_STROBE : return "Strobe";
- case ATT_CYAN : return "Cyan";
- case ATT_MAGENTA : return "Magenta";
- case ATT_YELLOW : return "Yellow";
- case ATT_RGOBO : return "RGobo";
- case ATT_GOBO : return "Gobo";
- case ATT_PRISM : return "Prism";
- case ATT_FOCUS : return "Focus";
- case ATT_ZOOM : return "Zoom";
- }
- return "(unknown)";
-}
-
-
-static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct nanolight *nl)
-{
- int w, h;
- int ch;
- int i;
- PangoContext *pc;
- PangoFontDescription *fontdesc;
- double x, y;
- PangoRectangle cursor;
-
- w = gtk_widget_get_allocated_width(widget);
- h = gtk_widget_get_allocated_height(widget);
- pc = gtk_widget_get_pango_context(widget);
-
- /* Overall background */
- cairo_set_source_rgb(cr, 0.0, 0.0, 0.2);
- cairo_paint(cr);
-
- /* Separator between fixture and cue areas */
- cairo_move_to(cr, w*OVERALL_SPLIT, OVERALL_BORDER);
- cairo_line_to(cr, w*OVERALL_SPLIT, h - OVERALL_BORDER);
- cairo_set_line_width(cr, 3.0);
- cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
- cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
- cairo_stroke(cr);
-
- /* Fixtures */
- cairo_save(cr);
- cairo_translate(cr, OVERALL_BORDER, OVERALL_BORDER);
- x = FIXTURE_BORDER;
- y = FIXTURE_BORDER;
- fontdesc = pango_font_description_from_string("Comfortaa Bold 8");
- for ( i=0; i<nl->n_fixtures; i++ ) {
- cairo_save(cr);
- cairo_translate(cr, x, y);
- cairo_scale(cr, nl->fixture_width/40.0, nl->fixture_width/40.0);
- draw_fixture(cr, pc, fontdesc, nl, &nl->fixtures[i]);
- cairo_restore(cr);
- x += nl->fixture_width + FIXTURE_BORDER*2;
- if ( x + nl->fixture_width + FIXTURE_BORDER*2 > w*OVERALL_SPLIT ) {
- x = FIXTURE_BORDER;
- y += nl->fixture_width*3.0/2.0 + FIXTURE_BORDER*2;
- }
- }
- cairo_restore(cr);
-
- /* Command line */
- pango_layout_set_text(nl->layout, nl->cmdline, -1);
- cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
- cairo_save(cr);
- cairo_translate(cr, OVERALL_BORDER, h - OVERALL_BORDER - 20.0);
- cairo_move_to(cr, 0.0, 0.0);
- pango_cairo_show_layout(cr, nl->layout);
- pango_layout_get_cursor_pos(nl->layout, nl->cursor_idx, &cursor, NULL);
- x = pango_units_to_double(cursor.x);
- y = pango_units_to_double(cursor.y);
- ch = pango_units_to_double(cursor.height);
- cairo_move_to(cr, x, y);
- cairo_line_to(cr, x, y+ch);
- cairo_set_line_width(cr, 3.0);
- cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
- cairo_set_source_rgb(cr, 0.8, 0.4, 0.4);
- cairo_stroke(cr);
- cairo_restore(cr);
-
- /* Selected attribute indicator */
- if ( nl->n_sel > 0 ) {
- pango_layout_set_text(nl->sa_layout, attr_text(nl->sel_attr), -1);
- cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
- pango_layout_set_width(nl->sa_layout, pango_units_from_double(200.0));
- cairo_move_to(cr, w - 200.0 - OVERALL_BORDER, h-OVERALL_BORDER-20.0);
- pango_cairo_show_layout(cr, nl->sa_layout);
- }
-
- return FALSE;
-}
-
static struct fixture *create_fixture(struct nanolight *nl, struct fixture_class *cls,
const char *label, int universe, int base_addr)
@@ -297,383 +80,6 @@ static struct fixture *create_fixture(struct nanolight *nl, struct fixture_class
}
-static void redraw(struct nanolight *nl)
-{
- gint w, h;
- w = gtk_widget_get_allocated_width(GTK_WIDGET(nl->da));
- h = gtk_widget_get_allocated_height(GTK_WIDGET(nl->da));
- gtk_widget_queue_draw_area(GTK_WIDGET(nl->da), 0, 0, w, h);
-}
-
-
-static void execute_command(struct nanolight *nl)
-{
- if ( command_run(nl->cmdline, nl) == 0 ) {
- nl->cmdline[0] = '\0';
- nl->cursor_idx = 0;
- }
- redraw(nl);
-}
-
-
-static gboolean im_commit_sig(GtkIMContext *im, gchar *str, struct nanolight *nl)
-{
- size_t cmd_len = strlen(nl->cmdline);
- if ( cmd_len+strlen(str) > 1023 ) return FALSE;
- strcat(nl->cmdline, str);
- nl->cursor_idx += strlen(str);
- redraw(nl);
- return FALSE;
-}
-
-
-static size_t delete_char(char *str)
-{
- char *last;
- size_t len;
- if ( str[0] == '\0' ) return 0;
- last = g_utf8_find_prev_char(str, str+strlen(str));
- len = strlen(last);
- last[0] = '\0';
- return len;
-}
-
-
-static void cap_value(struct fixture *fix, int n, signed int *v)
-{
- if ( *v < 0 ) *v = 0;
- if ( fix->cls->attrs[n].props & ATTR_16BIT ) {
- if ( *v > 65535 ) *v = 65535;
- } else {
- if ( *v > 255 ) *v = 255;
- }
-}
-
-
-static void set_start_attrs(struct nanolight *nl, enum attr_class cls)
-{
- int i;
- for ( i=0; i<nl->n_sel; i++ ) {
- int n;
- struct fixture *fix = &nl->fixtures[nl->selection[i]];
- if ( find_attribute(fix, cls, &n) ) {
- fix->attr_vals_start[n] = fix->attr_vals[n];
- }
- }
-
- /* If altering tilt, also change pan */
- if ( cls == ATT_TILT ) {
- set_start_attrs(nl, ATT_PAN);
- }
-}
-
-
-static gboolean button_press_sig(GtkWidget *da, GdkEventButton *event, struct nanolight *nl)
-{
-#if 0
- GdkSeat *seat;
- GdkWindow *win = gtk_widget_get_window(nl->da);
-
- seat = gdk_display_get_default_seat(gdk_display_get_default());
- nl->pointer = gdk_seat_get_pointer(seat);
-#endif
-
- set_start_attrs(nl, nl->sel_attr);
-
- nl->x_orig = event->x;
- nl->y_orig = event->y;
- nl->dragging = 1;
-
- return FALSE;
-}
-
-
-static gboolean button_release_sig(GtkWidget *da, GdkEventButton *event, struct nanolight *nl)
-{
- nl->dragging = 0;
- return FALSE;
-}
-
-
-static double maybe_fine(struct fixture *fix, int n, double inc, int shift)
-{
- if ( !(fix->cls->attrs[n].props & ATTR_16BIT) ) return inc;
- if ( shift ) return inc;
- return inc * 100.0;
-}
-
-
-static gboolean motion_sig(GtkWidget *da, GdkEventMotion *event, struct nanolight *nl)
-{
- int i;
- double x_inc, y_inc;
- int shift;
-
- if ( !nl->dragging ) return FALSE;
-
- x_inc = (event->x - nl->x_orig)/3;
- y_inc = (nl->y_orig - event->y)/3; /* Mouse up means increase */
-
- shift = event->state & GDK_SHIFT_MASK;
- if ( shift != nl->fine ) {
- nl->fine = shift;
- set_start_attrs(nl, nl->sel_attr);
- nl->x_orig = event->x;
- nl->y_orig = event->y;
- return FALSE;
- }
-
- if ( nl->sel_attr == ATT_TILT ) {
- for ( i=0; i<nl->n_sel; i++ ) {
- int n;
- struct fixture *fix = &nl->fixtures[nl->selection[i]];
- if ( find_attribute(fix, ATT_PAN, &n) ) {
- double inc = maybe_fine(fix, n, x_inc,
- event->state & GDK_SHIFT_MASK);
- signed int nv = fix->attr_vals_start[n] + inc;
- cap_value(fix, n, &nv);
- fix->attr_vals[n] = nv;
- }
- if ( find_attribute(fix, ATT_TILT, &n) ) {
- double inc = maybe_fine(fix, n, y_inc,
- event->state & GDK_SHIFT_MASK);
- signed int nv = fix->attr_vals_start[n] + inc;
- cap_value(fix, n, &nv);
- fix->attr_vals[n] = nv;
- }
- }
- } else {
- for ( i=0; i<nl->n_sel; i++ ) {
- int n;
- struct fixture *fix = &nl->fixtures[nl->selection[i]];
- if ( find_attribute(fix, nl->sel_attr, &n) ) {
- double inc = maybe_fine(fix, n, y_inc,
- event->state & GDK_SHIFT_MASK);
- signed int nv = fix->attr_vals_start[n] + inc;
- cap_value(fix, n, &nv);
- if ( !(fix->cls->attrs[n].props & ATTR_STOP) ) {
- fix->attr_vals[n] = nv;
- } else {
- printf("Can't change step attr with mouse\n");
- }
- }
- }
- }
-
- redraw(nl);
- return FALSE;
-}
-
-
-static void change_stop_attr(struct nanolight *nl, signed int inc)
-{
- int i;
- for ( i=0; i<nl->n_sel; i++ ) {
-
- struct fixture *fix = &nl->fixtures[nl->selection[i]];
- int n;
-
- if ( find_attribute(fix, nl->sel_attr, &n) ) {
- signed int nv;
- if ( !(fix->cls->attrs[n].props & ATTR_STOP) ) {
- printf("Can't change continuous attr with keys\n");
- continue;
- }
- nv = fix->attr_vals[n] + inc;
- if ( (nv>=0) && (nv<fix->cls->attrs[n].n_stops) ) {
- fix->attr_vals[n] = nv;
- }
- }
-
- }
- redraw(nl);
-}
-
-static void home_value(struct nanolight *nl)
-{
- int i;
- for ( i=0; i<nl->n_sel; i++ ) {
-
- struct fixture *fix = &nl->fixtures[nl->selection[i]];
- int n;
-
- if ( nl->sel_attr == ATT_TILT ) {
- if ( find_attribute(fix, ATT_PAN, &n) ) {
- fix->attr_vals[n] = fix->cls->attrs[n].home;
- }
- if ( find_attribute(fix, ATT_TILT, &n) ) {
- fix->attr_vals[n] = fix->cls->attrs[n].home;
- }
- nl->dragging = 0;
- } else {
- if ( find_attribute(fix, nl->sel_attr, &n) ) {
- fix->attr_vals[n] = fix->cls->attrs[n].home;
- }
- }
-
- }
- redraw(nl);
-}
-
-
-static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, struct nanolight *nl)
-{
- gboolean r;
- int claim = 1;
-
- switch ( event->keyval ) {
-
- case GDK_KEY_Left :
- break;
-
- case GDK_KEY_Right :
- break;
-
- case GDK_KEY_Up :
- change_stop_attr(nl, +1);
- break;
-
- case GDK_KEY_Down :
- change_stop_attr(nl, -1);
- break;
-
- case GDK_KEY_Return :
- execute_command(nl);
- break;
-
- case GDK_KEY_Escape :
- nl->cmdline[0] = '\0';
- nl->cursor_idx = 0;
- nl->n_sel = 0;
- nl->dragging = 0;
- break;
-
- case GDK_KEY_BackSpace :
- nl->cursor_idx -= delete_char(nl->cmdline);
- break;
-
- case GDK_KEY_KP_Enter :
- printf("Go!\n");
- break;
-
- case GDK_KEY_KP_Add :
- printf("Stop/back!\n");
- break;
-
- case GDK_KEY_Home :
- home_value(nl);
- break;
-
- case GDK_KEY_F1 :
- nl->sel_attr = key_attrs[1];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F2 :
- nl->sel_attr = key_attrs[2];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F3 :
- nl->sel_attr = key_attrs[3];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F4 :
- nl->sel_attr = key_attrs[4];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F5 :
- nl->sel_attr = key_attrs[5];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F6 :
- nl->sel_attr = key_attrs[6];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F7 :
- nl->sel_attr = key_attrs[7];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F8 :
- nl->sel_attr = key_attrs[8];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F9 :
- nl->sel_attr = key_attrs[9];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F10 :
- nl->sel_attr = key_attrs[10];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F11 :
- nl->sel_attr = key_attrs[11];
- nl->dragging = 0;
- break;
-
- case GDK_KEY_F12 :
- nl->sel_attr = key_attrs[12];
- nl->dragging = 0;
- break;
-
- default :
- claim = 0;
- break;
-
- }
-
- if ( !claim ) {
- /* Throw the event to the IM context and let it sort things out */
- r = gtk_im_context_filter_keypress(GTK_IM_CONTEXT(nl->im_context), event);
- if ( r ) claim = 1;
- } else {
- redraw(nl);
- }
-
- if ( claim ) return TRUE;
- return FALSE;
-}
-
-
-static gint realise_sig(GtkWidget *da, struct nanolight *nl)
-{
- GdkWindow *win = gtk_widget_get_window(da);
- PangoContext *pc;
- PangoFontDescription *fontdesc;
-
- /* Keyboard and input method stuff */
- nl->im_context = gtk_im_multicontext_new();
- gtk_im_context_set_client_window(GTK_IM_CONTEXT(nl->im_context), win);
- gdk_window_set_accept_focus(win, TRUE);
- g_signal_connect(G_OBJECT(nl->im_context), "commit", G_CALLBACK(im_commit_sig), nl);
- g_signal_connect(G_OBJECT(da), "key-press-event", G_CALLBACK(key_press_sig), nl);
- g_signal_connect(G_OBJECT(da), "button-press-event", G_CALLBACK(button_press_sig), nl);
- g_signal_connect(G_OBJECT(da), "button-release-event", G_CALLBACK(button_release_sig), nl);
- g_signal_connect(G_OBJECT(da), "motion-notify-event", G_CALLBACK(motion_sig), nl);
-
- pc = gtk_widget_get_pango_context(da);
- fontdesc = pango_font_description_from_string("Comfortaa Bold 16");
-
- nl->layout = pango_layout_new(pc);
- pango_layout_set_alignment(nl->layout, PANGO_ALIGN_LEFT);
- pango_layout_set_font_description(nl->layout, fontdesc);
-
- nl->sa_layout = pango_layout_new(pc);
- pango_layout_set_alignment(nl->sa_layout, PANGO_ALIGN_RIGHT);
- pango_layout_set_font_description(nl->sa_layout, fontdesc);
-
- return FALSE;
-}
-
-
static gboolean scanout_cb(gpointer data)
{
scanout_all((struct nanolight *)data);
@@ -687,8 +93,6 @@ int main(int argc, char *argv[])
struct fixture_class cls;
struct attribute attrs[128];
int c;
- GtkWidget *mainwindow;
- GtkWidget *da;
gtk_init(&argc, &argv);
@@ -813,23 +217,7 @@ int main(int argc, char *argv[])
/* Set up output */
g_timeout_add(100, scanout_cb, &nl);
- /* Create main window */
- mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- gtk_window_fullscreen(GTK_WINDOW(mainwindow));
- g_signal_connect_swapped(G_OBJECT(mainwindow), "destroy", gtk_main_quit, NULL);
-
- da = gtk_drawing_area_new();
- nl.da = da;
- gtk_container_add(GTK_CONTAINER(mainwindow), GTK_WIDGET(da));
- gtk_widget_set_can_focus(GTK_WIDGET(da), TRUE);
- gtk_widget_add_events(da, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
- | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- | GDK_BUTTON_MOTION_MASK);
- g_signal_connect(G_OBJECT(da), "draw", G_CALLBACK(draw_sig), &nl);
- g_signal_connect(G_OBJECT(da), "realize", G_CALLBACK(realise_sig), &nl);
-
- gtk_widget_grab_focus(GTK_WIDGET(da));
- gtk_widget_show_all(mainwindow);
+ create_main_window(&nl);
gtk_main();
return 0;