From 1e32c49b4d08b1d517ccda7380c3909986d9d7cd Mon Sep 17 00:00:00 2001 From: Thomas White Date: Tue, 28 May 2019 22:53:11 +0200 Subject: Command line and rendering stuff --- meson.build | 1 + src/command.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/command.h | 28 ++++++++ src/nanolight.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- src/nanolight.h | 8 +++ 5 files changed, 417 insertions(+), 12 deletions(-) create mode 100644 src/command.c create mode 100644 src/command.h diff --git a/meson.build b/meson.build index d939dac..96aea93 100644 --- a/meson.build +++ b/meson.build @@ -24,6 +24,7 @@ mdep = cc.find_library('m', required : false) # Main program executable('nanolight', ['src/nanolight.c', + 'src/command.c', ], dependencies : [gtk_dep, mdep], install : true) diff --git a/src/command.c b/src/command.c new file mode 100644 index 0000000..7ae36ae --- /dev/null +++ b/src/command.c @@ -0,0 +1,185 @@ +/* + * command.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 +#define _(x) gettext(x) + +#include "nanolight.h" + +enum token_type +{ + TK_FIXTURE, + TK_AT, + TK_DOT, + TK_TO, + TK_LEVEL, + TK_ATTRIBUTE, +}; + + +struct token +{ + enum token_type type; + int fixture_index; + int val; +}; + + +static int stop_char(char c) +{ + if ( c == '@' ) return 1; + if ( c == ' ' ) return 1; + if ( c == '\0' ) return 1; + if ( c == '.' ) return 1; + if ( c == '-' ) return 1; + return 0; +} + + +static int find_tokens(const char *cmd, struct token *tokens, struct nanolight *nl) +{ + int i; + int n = 0; + int pos = 0; + do { + int start; + char *word; + unsigned long val; + char *endptr; + int done = 0; + + while ( isspace(cmd[pos]) ) pos++; + start = pos; + + /* Is it an AT? */ + if ( cmd[pos] == '@' ) { + tokens[n++].type = TK_AT; + pos++; + continue; + } + + /* Is it a dot? */ + if ( cmd[pos] == '.' ) { + tokens[n++].type = TK_DOT; + pos++; + continue; + } + + /* Is it a dash? */ + if ( cmd[pos] == '-' ) { + tokens[n++].type = TK_TO; + pos++; + continue; + } + + while ( !stop_char(cmd[pos]) ) pos++; + word = strndup(cmd+start, pos-start); + + /* Is is a fixture name? */ + for ( i=0; in_fixtures; i++ ) { + if ( strcasecmp(nl->fixtures[i].label, word) == 0 ) { + tokens[n].fixture_index = i; + tokens[n++].type = TK_FIXTURE; + done = 1; + break; + } + } + + /* Is is an attribute name? */ + for ( i=0; in_fixtures; i++ ) { + if ( strcasecmp(nl->fixtures[i].label, word) == 0 ) { + tokens[n].fixture_index = i; + tokens[n++].type = TK_FIXTURE; + done = 1; + break; + } + } + + /* Is it a number? */ + val = strtoul(word, &endptr, 10); + if ( (word[0] != '\0') && (endptr[0] == '\0') ) { + tokens[n].val = val; + tokens[n++].type = TK_LEVEL; + done = 1; + } + + free(word); + + if ( !done ) return 0; + + } while ( cmd[pos] != '\0' ); + return n; +} + + +static void show_tokens(struct token *tokens, int n, struct nanolight *nl) +{ + int i; + + for ( i=0; ifixtures[tokens[i].fixture_index].label); + break; + + case TK_AT: + printf(" [@]"); + break; + + case TK_DOT: + printf(" [.]"); + break; + + case TK_TO: + printf(" [-]"); + break; + + case TK_LEVEL: + printf(" [value:%i]", tokens[i].val); + break; + + } + } + + printf("\n"); +} + + +int command_run(const char *cmd, struct nanolight *nl) +{ + struct token tokens[1024]; + int n; + + n = find_tokens(cmd, tokens, nl); + if ( n == 0 ) return 1; + + show_tokens(tokens, n, nl); + + return 0; +} + diff --git a/src/command.h b/src/command.h new file mode 100644 index 0000000..2d540d4 --- /dev/null +++ b/src/command.h @@ -0,0 +1,28 @@ +/* + * command.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 COMMAND_H +#define COMMAND_H + +extern int command_run(const char *cmd, struct nanolight *nl); + +#endif /* COMMAND_H */ diff --git a/src/nanolight.c b/src/nanolight.c index 4c657df..6cb06e6 100644 --- a/src/nanolight.c +++ b/src/nanolight.c @@ -33,6 +33,7 @@ #define _(x) gettext(x) #include "nanolight.h" +#include "command.h" static void show_help(const char *s) { @@ -43,12 +44,28 @@ static void show_help(const char *s) #define OVERALL_BORDER (20.0) #define OVERALL_SPLIT (0.5) +#define FIXTURE_BORDER (5.0) + +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 void draw_fixture(cairo_t *cr, PangoContext *pc, PangoFontDescription *fontdesc, - struct nanolight *nl, struct fixture *fx) + struct nanolight *nl, struct fixture *fix) { PangoLayout *layout; - const double w = nl->fixture_width; + const double w = 40.0; + char tmp[32]; cairo_rectangle(cr, 0.5, 0.5, w, 3.0/2.0*w); cairo_set_source_rgb(cr, 0.3, 0.3, 0.3); @@ -57,13 +74,28 @@ static void draw_fixture(cairo_t *cr, PangoContext *pc, PangoFontDescription *fo cairo_set_line_width(cr, 1.0); cairo_stroke(cr); + /* Label */ layout = pango_layout_new(pc); - pango_layout_set_text(layout, fx->label, -1); - pango_layout_set_width(layout, w*PANGO_SCALE); + 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); } @@ -73,11 +105,12 @@ static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct nanolight *nl) int i; PangoContext *pc; PangoFontDescription *fontdesc; + PangoLayout *layout; + double x, y; w = gtk_widget_get_allocated_width(widget); h = gtk_widget_get_allocated_height(widget); pc = gtk_widget_get_pango_context(widget); - fontdesc = pango_font_description_from_string("Comfortaa Bold 14"); /* Overall background */ cairo_set_source_rgb(cr, 0.0, 0.0, 0.2); @@ -93,11 +126,153 @@ static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct nanolight *nl) /* Fixtures */ 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; + } } /* Command line */ + layout = pango_layout_new(pc); + fontdesc = pango_font_description_from_string("Comfortaa Bold 16"); + pango_layout_set_text(layout, nl->cmdline, -1); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_font_description(layout, fontdesc); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_move_to(cr, 0.0, h - OVERALL_BORDER*2 - 20.0); + pango_cairo_show_layout(cr, layout); + g_object_unref(layout); + + return FALSE; +} + + +static struct fixture *create_fixture(struct nanolight *nl, struct fixture_class *cls, + const char *label, int base_addr) +{ + struct fixture *fix; + + if ( nl->n_fixtures == nl->max_fixtures ) { + struct fixture *fixtures_new; + fixtures_new = realloc(nl->fixtures, (64+nl->max_fixtures)*sizeof(struct fixture)); + if ( fixtures_new == NULL ) return NULL; + nl->fixtures = fixtures_new; + nl->max_fixtures += 64; + } + + fix = &nl->fixtures[nl->n_fixtures++]; + fix->label = strdup(label); + fix->base_addr = base_addr; + fix->cls = cls; + fix->attr_vals = calloc(cls->n_attrs, sizeof(int)); + if ( fix->attr_vals == NULL ) { + nl->n_fixtures--; + return NULL; + } + return fix; +} + + +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'; + } + 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); + redraw(nl); + return FALSE; +} + + +static void delete_char(char *str) +{ + char *last; + if ( str[0] == '\0' ) return; + last = g_utf8_find_prev_char(str, str+strlen(str)); + last[0] = '\0'; +} + + +static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, struct nanolight *nl) +{ + gboolean r; + int claim = 0; + + /* 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 ) return FALSE; /* IM ate it */ + + switch ( event->keyval ) { + + case GDK_KEY_Left : + claim = 1; + break; + + case GDK_KEY_Right : + claim = 1; + break; + + case GDK_KEY_Return : + execute_command(nl); + claim = 1; + break; + + case GDK_KEY_Escape : + nl->cmdline[0] = '\0'; + redraw(nl); + claim = 1; + break; + + case GDK_KEY_BackSpace : + delete_char(nl->cmdline); + claim = 1; + redraw(nl); + break; + + } + + if ( claim ) return TRUE; + return FALSE; +} + + +static gint realise_sig(GtkWidget *da, struct nanolight *nl) +{ + GdkWindow *win = gtk_widget_get_window(da); + + /* 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); return FALSE; } @@ -106,7 +281,6 @@ static gboolean draw_sig(GtkWidget *widget, cairo_t *cr, struct nanolight *nl) int main(int argc, char *argv[]) { struct nanolight nl; - struct fixture fix; struct fixture_class cls; struct attribute attrs[3]; int c; @@ -163,21 +337,30 @@ int main(int argc, char *argv[]) cls.attrs[2].props = ATTR_16BIT; cls.attrs[2].addr_offset = 2; - nl.n_fixtures = 1; - nl.fixtures = &fix; - fix.label = "mh1"; - fix.cls = &cls; - nl.fixture_width = 80.0; + nl.fixtures = NULL; + nl.n_fixtures = 0; + nl.max_fixtures = 0; + nl.cmdline[0] = '\0'; + + create_fixture(&nl, &cls, "mh1", 1); + create_fixture(&nl, &cls, "mh2", 52); + create_fixture(&nl, &cls, "mh3", 103); + create_fixture(&nl, &cls, "mh4", 154)->attr_vals[0]=255; mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_fullscreen(GTK_WINDOW(mainwindow)); + //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_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_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); gtk_main(); diff --git a/src/nanolight.h b/src/nanolight.h index 5235666..29cdbfc 100644 --- a/src/nanolight.h +++ b/src/nanolight.h @@ -23,6 +23,8 @@ #ifndef NANOLIGHT_H #define NANOLIGHT_H +#include + /* Attribute flags */ #define ATTR_NONE (0) #define ATTR_16BIT (1) @@ -57,15 +59,21 @@ struct fixture char *label; int base_addr; struct fixture_class *cls; + int *attr_vals; }; struct nanolight { int n_fixtures; + int max_fixtures; struct fixture *fixtures; + GtkIMContext *im_context; + GtkWidget *da; + double fixture_width; + char cmdline[1024]; }; -- cgit v1.2.3