From 079baff220a963c365ab8448c421e22e896caaf1 Mon Sep 17 00:00:00 2001 From: cbdev Date: Wed, 18 Sep 2019 23:44:36 +0200 Subject: Fix maweb command key handling --- backends/maweb.c | 189 +++++++++++++++++++++++++++++++---------------------- backends/maweb.h | 18 ++++- backends/maweb.md | 76 ++++++++++++++------- backends/winmidi.c | 3 +- 4 files changed, 181 insertions(+), 105 deletions(-) diff --git a/backends/maweb.c b/backends/maweb.c index c88ca8c..be356d0 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -19,72 +19,57 @@ static uint64_t update_interval = 50; static uint64_t last_update = 0; static uint64_t updates_inflight = 0; -static char* cmdline_keys[] = { - "SET", - "PREV", - "NEXT", - "CLEAR", - "FIXTURE_CHANNEL", - "FIXTURE_GROUP_PRESET", - "EXEC_CUE", - "STORE_UPDATE", - "OOPS", - "ESC", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "PUNKT", - "PLUS", - "MINUS", - "THRU", - "IF", - "AT", - "FULL", - "HIGH", - "ENTER", - "OFF", - "ON", - "ASSIGN", - "LABEL", - "COPY", - "TIME", - "PAGE", - "MACRO", - "DELETE", - "GOTO", - "GO_PLUS", - "GO_MINUS", - "PAUSE", - "SELECT", - "FIXTURE", - "SEQU", - "CUE", - "PRESET", - "EDIT", - "UPDATE", - "EXEC", - "STORE", - "GROUP", - "PROG_ONLY", - "SPECIAL_DIALOGUE", - "SOLO", - "ODD", - "EVEN", - "WINGS", - "RESET", - "MA", - "layerMode", - "featureSort", - "fixtureSort", - "channelSort", - "hideName" +static maweb_command_key cmdline_keys[] = { + {"PREV", 109, 0, 1}, {"SET", 108, 1, 0, 1}, {"NEXT", 110, 0, 1}, + {"TIME", 58, 1, 1}, {"EDIT", 55, 1, 1}, {"UPDATE", 57, 1, 1}, + {"OOPS", 53, 1, 1}, {"ESC", 54, 1, 1}, {"CLEAR", 105, 1, 1}, + {"0", 86, 1, 1}, {"1", 87, 1, 1}, {"2", 88, 1, 1}, + {"3", 89, 1, 1}, {"4", 90, 1, 1}, {"5", 91, 1, 1}, + {"6", 92, 1, 1}, {"7", 93, 1, 1}, {"8", 94, 1, 1}, + {"9", 95, 1, 1}, {"PUNKT", 98, 1, 1}, {"ENTER", 106, 1, 1}, + {"PLUS", 96, 1, 1}, {"MINUS", 97, 1, 1}, {"THRU", 102, 1, 1}, + {"IF", 103, 1, 1}, {"AT", 104, 1, 1}, {"FULL", 99, 1, 1}, + {"MA", 68, 0, 1}, {"HIGH", 100, 1, 1, 1}, {"SOLO", 101, 1, 1, 1}, + {"SELECT", 42, 1, 1}, {"OFF", 43, 1, 1}, {"ON", 46, 1, 1}, + {"ASSIGN", 63, 1, 1}, {"LABEL", 0, 1, 1}, + {"COPY", 73, 1, 1}, {"DELETE", 69, 1, 1}, {"STORE", 59, 1, 1}, + {"GOTO", 56, 1, 1}, {"PAGE", 70, 1, 1}, {"MACRO", 71, 1, 1}, + {"PRESET", 72, 1, 1}, {"SEQU", 74, 1, 1}, {"CUE", 75, 1, 1}, + {"EXEC", 76, 1, 1}, {"FIXTURE", 83, 1, 1}, {"GROUP", 84, 1, 1}, + {"GO_MINUS", 10, 1, 1}, {"PAUSE", 9, 1, 1}, {"GO_PLUS", 11, 1, 1}, + + {"FIXTURE_CHANNEL", 0, 1, 1}, {"FIXTURE_GROUP_PRESET", 0, 1, 1}, + {"EXEC_CUE", 0, 1, 1}, {"STORE_UPDATE", 0, 1, 1}, {"PROG_ONLY", 0, 1, 1, 1}, + {"SPECIAL_DIALOGUE", 0, 1, 1}, + {"ODD", 0, 1, 1}, {"EVEN", 0, 1, 1}, + {"WINGS", 0, 1, 1}, {"RESET", 0, 1, 1}, + //gma2 internal only + {"CHPGPLUS", 3}, {"CHPGMINUS", 4}, + {"FDPGPLUS", 5}, {"FDPGMINUS", 6}, + {"BTPGPLUS", 7}, {"BTPGMINUS", 8}, + {"X1", 12}, {"X2", 13}, {"X3", 14}, + {"X4", 15}, {"X5", 16}, {"X6", 17}, + {"X7", 18}, {"X8", 19}, {"X9", 20}, + {"X10", 21}, {"X11", 22}, {"X12", 23}, + {"X13", 24}, {"X14", 25}, {"X15", 26}, + {"X16", 27}, {"X17", 28}, {"X18", 29}, + {"X19", 30}, {"X20", 31}, + {"V1", 120}, {"V2", 121}, {"V3", 122}, + {"V4", 123}, {"V5", 124}, {"V6", 125}, + {"V7", 126}, {"V8", 127}, {"V9", 128}, + {"V10", 129}, + {"NIPPLE", 40}, + {"TOOLS", 119}, {"SETUP", 117}, {"BACKUP", 117}, + {"BLIND", 60}, {"FREEZE", 61}, {"PREVIEW", 62}, + {"FIX", 41}, {"TEMP", 44}, {"TOP", 45}, + {"VIEW", 66}, {"EFFECT", 67}, {"CHANNEL", 82}, + {"MOVE", 85}, {"BLACKOUT", 65}, + {"PLEASE", 106}, + {"LIST", 32}, {"USER1", 33}, {"USER2", 34}, + {"ALIGN", 64}, {"HELP", 116}, + {"UP", 107}, {"DOWN", 111}, + {"FASTREVERSE", 47}, {"LEARN", 48}, {"FASTFORWARD", 49}, + {"GO_MINUS_SMALL", 50}, {"PAUSE_SMALL", 51}, {"GO_PLUS_SMALL", 52} }; int init(){ @@ -199,6 +184,22 @@ static int maweb_configure_instance(instance* inst, char* option, char* value){ return 1; #endif } + else if(!strcmp(option, "cmdline")){ + if(!strcmp(value, "console")){ + data->cmdline = cmd_console; + } + else if(!strcmp(value, "remote")){ + data->cmdline = cmd_remote; + } + else if(!strcmp(value, "downgrade")){ + data->cmdline = cmd_downgrade; + } + else{ + fprintf(stderr, "Unknown maweb commandline mode %s for instance %s\n", value, inst->name); + return 1; + } + return 0; + } fprintf(stderr, "Unknown configuration parameter %s for maweb instance %s\n", option, inst->name); return 1; @@ -268,10 +269,15 @@ static channel* maweb_channel(instance* inst, char* spec){ chan.index = strtoul(next_token, NULL, 10); } else{ - for(n = 0; n < sizeof(cmdline_keys) / sizeof(char*); n++){ - //FIXME this is broken for layerMode - if(!strcmp(spec, cmdline_keys[n]) || (*spec == 'l' && !strcmp(spec + 1, cmdline_keys[n]))){ - chan.type = (*spec == 'l') ? cmdline_local : cmdline; + for(n = 0; n < sizeof(cmdline_keys) / sizeof(maweb_command_key); n++){ + if(!strcmp(spec, cmdline_keys[n].name)){ + if((data->cmdline == cmd_remote && !cmdline_keys[n].press && !cmdline_keys[n].release) + || (data->cmdline == cmd_console && !cmdline_keys[n].lua)){ + fprintf(stderr, "maweb cmdline key %s does not work with the current commandline mode for instance %s\n", spec, inst->name); + return NULL; + } + + chan.type = cmdline; chan.index = n + 1; chan.page = 1; break; @@ -599,6 +605,8 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le field = json_obj_str(payload, "appType", NULL); if(!strncmp(field, "dot2", 4)){ data->peer_type = peer_dot2; + //the dot2 can't handle lua commands + data->cmdline = cmd_remote; } else if(!strncmp(field, "gma2", 4)){ data->peer_type = peer_ma2; @@ -860,14 +868,41 @@ static int maweb_set(instance* inst, size_t num, channel** c, channel_value* v){ data->session); break; case cmdline: - snprintf(xmit_buffer, sizeof(xmit_buffer), - "{\"keyname\":\"%s\"," - //"\"autoSubmit\":false," - "\"value\":%d" - "}", cmdline_keys[chan->index], - (v[n].normalised > 0.9) ? 1 : 0); + if(cmdline_keys[chan->index].lua + && (data->cmdline == cmd_console || data->cmdline == cmd_downgrade) + && data->peer_type != peer_dot2){ + //push canbus events + snprintf(xmit_buffer, sizeof(xmit_buffer), + "{\"command\":\"LUA 'gma.canbus.hardkey(%d, %s, false)'\"," + "\"requestType\":\"command\"," + "\"session\":%" PRIu64 + "}", cmdline_keys[chan->index].lua, + (v[n].normalised > 0.9) ? "true" : "false", + data->session); + } + else if((cmdline_keys[chan->index].press || cmdline_keys[chan->index].release) + && (data->cmdline != cmd_console)){ + //send press/release events if required + if((cmdline_keys[chan->index].press && v[n].normalised > 0.9) + || (cmdline_keys[chan->index].release && v[n].normalised < 0.9)){ + snprintf(xmit_buffer, sizeof(xmit_buffer), + "{\"keyname\":\"%s\"," + "\"autoSubmit\":%s," + "\"value\":%d" + "}", cmdline_keys[chan->index].name, + cmdline_keys[chan->index].auto_submit ? "true" : "null", + (v[n].normalised > 0.9) ? 1 : 0); + } + else{ + continue; + } + } + else{ + fprintf(stderr, "maweb commandline key %s not executed on %s due to mode mismatch\n", + cmdline_keys[chan->index].name, inst->name); + continue; + } break; - //TODO cmdline_local default: fprintf(stderr, "maweb control not yet implemented\n"); return 1; diff --git a/backends/maweb.h b/backends/maweb.h index 3738367..b9de68f 100644 --- a/backends/maweb.h +++ b/backends/maweb.h @@ -25,8 +25,7 @@ typedef enum /*_maweb_channel_type*/ { exec_button = 2, //gma: 0 dot: 0 exec_lower = 3, //gma: 1 dot: 1 exec_upper = 4, //gma: 2 dot: 0 - cmdline, - cmdline_local + cmdline } maweb_channel_type; typedef enum /*_maweb_peer_type*/ { @@ -43,6 +42,12 @@ typedef enum /*_ws_conn_state*/ { ws_closed } maweb_state; +typedef enum /*_maweb_cmdline_mode*/ { + cmd_remote = 0, + cmd_console, + cmd_downgrade +} maweb_cmdline_mode; + typedef enum /*_ws_frame_op*/ { ws_text = 1, ws_binary = 2, @@ -50,6 +55,14 @@ typedef enum /*_ws_frame_op*/ { ws_pong = 10 } maweb_operation; +typedef struct { + char* name; + unsigned lua; + uint8_t press; + uint8_t release; + uint8_t auto_submit; +} maweb_command_key; + typedef struct /*_maweb_channel*/ { maweb_channel_type type; uint16_t page; @@ -73,6 +86,7 @@ typedef struct /*_maweb_instance_data*/ { size_t channels; maweb_channel_data* channel; + maweb_cmdline_mode cmdline; int fd; maweb_state state; diff --git a/backends/maweb.md b/backends/maweb.md index 93cd776..a18cde4 100644 --- a/backends/maweb.md +++ b/backends/maweb.md @@ -26,6 +26,14 @@ Web Remote. Set a web remote password using the option below the activation sett | `host` | `10.23.42.21 80` | none | Host address (and optional port) of the MA Web Remote | | `user` | `midimonster` | none | User for the remote session (GrandMA2) | | `password` | `midimonster` | `midimonster` | Password for the remote session | +| `cmdline` | `console` | `remote` | Commandline key handling mode (see below) | + +The per-instance command line mode may be one of `remote`, `console` or `downgrade`. The first option handles +command keys with a "virtual" commandline belonging to the Web Remote connection. Any commands entered are +not visible on the main console. The `console` mode is only available with GrandMA2 remotes and injects the +key events into the main console's command line. When connected to a dot2 console, the use of commandline keys +will not be possible. With the `downgrade` mode, keys are handled on the console if possible, falling back to +remote handling if not. #### Channel specification @@ -33,7 +41,7 @@ Currently, three types of MA controls can be assigned, with each having some sub * Fader executor * Button executor -* Command line buttons +* Command keys ##### Executors @@ -70,33 +78,52 @@ mw1.page2.button103 > mw1.page3.fader101 mw1.page2.button803 > mw1.page3.button516 ``` -##### Command line buttons +##### Command keys -Command line buttons will be pressed when the incoming event value is greater than `0.9` and released when it is less than that. +Command keys will be pressed when the incoming event value is greater than `0.9` and released when it is less than that. They can be mapped using the syntax ``` -mw1. +mw1. ``` -The following button names are recognized by the backend: - -| Supported | Command | Line | Keys | | | | -|---------------|---------------|---------------|---------------|-----------------------|-------------------------------|---------------| -| `SET` | `PREV` | `NEXT` | `CLEAR` | `FIXTURE_CHANNEL` | `FIXTURE_GROUP_PRESET` | `EXEC_CUE` | -| `STORE_UPDATE`| `OOPS` | `ESC` | `OFF` | `ON` | `MA` | `STORE` | -| `0` | `1` | `2` | `3` | `4` | `5` | `6` | -| `7` | `8` | `9` | `PUNKT` | `PLUS` | `MINUS` | `THRU` | -| `IF` | `AT` | `FULL` | `HIGH` | `ENTER` | `ASSIGN` | `LABEL` | -| `COPY` | `TIME` | `PAGE` | `MACRO` | `DELETE` | `GOTO` | `GO_PLUS` | -| `GO_MINUS` | `PAUSE` | `SELECT` | `FIXTURE` | `SEQU` | `CUE` | `PRESET` | -| `EDIT` | `UPDATE` | `EXEC` | `GROUP` | `PROG_ONLY` | `SPECIAL_DIALOGUE` | `SOLO` | -| `ODD` | `EVEN` | `WINGS` | `RESET` | `layerMode` | `featureSort` | `fixtureSort` | -| `channelSort` | `hideName` | | | | | | - -Note that each Web Remote connection has it's own command line, as such commands entered using this backend will not affect -the command line on the main console. To do that, you will need to use another backend to feed input to the MA, such as -the ArtNet or MIDI backends. +The following keys are mappable in all commandline modes and work on all consoles + +| Supported | Command | Line | Keys | | | +|---------------|---------------|---------------|---------------|---------------|---------------| +| `PREV` | `SET` | `NEXT` | `TIME` | `EDIT` | `UPDATE` | +| `OOPS` | `ESC` | `CLEAR` | `0` | `1` | `2` | +| `3` | `4` | `5` | `6` | `7` | `8` | +| `9` | `PUNKT` | `ENTER` | `PLUS` | `MINUS` | `THRU` | +| `IF` | `AT` | `FULL` | `MA` | `HIGH` | `SOLO` | +| `SELECT` | `OFF` | `ON` | `ASSIGN` | `COPY` | `DELETE` | +| `STORE` | `GOTO` | `PAGE` | `MACRO` | `PRESET` | `SEQU` | +| `CUE` | `EXEC` | `FIXTURE` | `GROUP` | `GO_MINUS` | `PAUSE` | +| `GO_PLUS` | | | | | | + +The following keys only work in the `remote` or `downgrade` commandline mode, but on all consoles + +| Web | Remote | specific | | | +|---------------|-----------------------|-------------------------------|---------------|-----------------------| +| `LABEL` |`FIXTURE_CHANNEL` | `FIXTURE_GROUP_PRESET` | `EXEC_CUE` | `STORE_UPDATE` | +| `PROG_ONLY` | `SPECIAL_DIALOGUE` | `ODD` | `EVEN` | `WINGS` | +| `RESET` | | | | | + +The following keys only work in the `console` or `downgrade` command line modes on a GrandMA2 + +| GrandMA2 | console | only | | | | +|---------------|---------------|---------------|---------------|---------------|---------------| +| `CHPGPLUS` | `CHPGMINUS` | `FDPGPLUS` | `FDPGMINUS` | `BTPGPLUS` | `BTPGMINUS` | +| `X1` | `X2` | `X3` | `X4` | `X5` | `X6` | +| `X7` | `X8` | `X9` | `X10` | `X11` | `X12` | +| `X13` | `X14` | `X15` | `X16` | `X17` | `X18` | +| `X19` | `X20` | `V1` | `V2` | `V3` | `V4` | +| `V5` | `V6` | `V7` | `V8` | `V9` | `V10` | +| `NIPPLE` | `TOOLS` | `SETUP` | `BACKUP` | `BLIND` | `FREEZE` | +| `PREVIEW` | `FIX` | `TEMP` | `TOP` | `VIEW` | `EFFECT` | +| `CHANNEL` | `MOVE` | `BLACKOUT` | `PLEASE` | `LIST` | `USER1` | +| `USER2` | `ALIGN` | `HELP` | `UP` | `DOWN` | `FASTREVERSE` | +| `LEARN` | `FASTFORWARD` | `GO_MINUS_SMALL` | `PAUSE_SMALL` | `GO_PLUS_SMALL` | | #### Known bugs / problems @@ -109,6 +136,5 @@ Data input from the console is done by actively querying the state of all mapped at low latency. A lower input interval value will produce data with lower latency, at the cost of network & CPU usage. Higher values will make the input "step" more, but will not consume as many CPU cycles and network bandwidth. -When requesting button executor events on the fader pages (execs 101 to 222) of a dot2 console, map at least one fader control from the 0 - 22 range or input will not work due to strange limitations in the MA Web API. - -Command line events are sent, but I'm not sure they're being handled yet. +When requesting button executor events on the fader pages (execs 101 to 222) of a dot2 console, map at least one fader control from the 0 - 22 range +or input will not work due to strange limitations in the MA Web API. diff --git a/backends/winmidi.c b/backends/winmidi.c index 67187ac..13c4b4a 100644 --- a/backends/winmidi.c +++ b/backends/winmidi.c @@ -21,6 +21,7 @@ static struct { }; //TODO detect option +//TODO receive feedback socket until EAGAIN int init(){ backend winmidi = { @@ -465,7 +466,7 @@ static int winmidi_start(){ } //open the feedback sockets - //for some reason this while construct fails to work on 'real' windows with ipv6 + //for some reason the feedback connection fails to work on 'real' windows with ipv6 backend_config.socket_pair[0] = mmbackend_socket("127.0.0.1", "0", SOCK_DGRAM, 1, 0); if(backend_config.socket_pair[0] < 0){ fprintf(stderr, "winmidi failed to open feedback socket\n"); -- cgit v1.2.3