From 40130e129b523283e77dc7546e3a32671e0e1541 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Mon, 17 Jun 2019 23:21:28 +0200 Subject: Move UI stuff to a separate file --- meson.build | 1 + src/display.c | 655 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/display.h | 28 +++ src/nanolight.c | 616 +--------------------------------------------------- 4 files changed, 686 insertions(+), 614 deletions(-) create mode 100644 src/display.c create mode 100644 src/display.h 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 + * + * 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 . + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#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; icls->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; in_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; jcls->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; in_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; in_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; in_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; in_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; in_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) && (nvcls->attrs[n].n_stops) ) { + fix->attr_vals[n] = nv; + } + } + + } + redraw(nl); +} + +static void home_value(struct nanolight *nl) +{ + int i; + for ( i=0; in_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 + * + * 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 . + * + */ + +#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; icls->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; in_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; jcls->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; in_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; in_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; in_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; in_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; in_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) && (nvcls->attrs[n].n_stops) ) { - fix->attr_vals[n] = nv; - } - } - - } - redraw(nl); -} - -static void home_value(struct nanolight *nl) -{ - int i; - for ( i=0; in_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; -- cgit v1.2.3