From 100b8f2ea0f269094331ee39699ef5fb3e571584 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 16 May 2020 16:18:19 +0200 Subject: Implement maweb dynamic interval --- backends/maweb.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 97d4cea..980a914 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -1,4 +1,5 @@ #define BACKEND_NAME "maweb" +//#define DEBUG #include #include @@ -15,14 +16,9 @@ #define WS_FLAG_FIN 0x80 #define WS_FLAG_MASK 0x80 -/* - * TODO handle peer close/unregister/reopen and fallback connections - */ - static uint64_t last_keepalive = 0; -static uint64_t update_interval = 50; +static uint64_t update_interval = 0; static uint64_t last_update = 0; -static uint64_t updates_inflight = 0; static uint64_t quiet_mode = 0; static maweb_command_key cmdline_keys[] = { @@ -136,7 +132,10 @@ static int channel_comparator(const void* raw_a, const void* raw_b){ } static uint32_t maweb_interval(){ - return update_interval - (last_update % update_interval); + if(update_interval){ + return update_interval - (last_update % update_interval); + } + return 0; } static int maweb_configure(char* option, char* value){ @@ -423,6 +422,7 @@ static int maweb_process_playback(instance* inst, int64_t page, maweb_channel_ty } static int maweb_process_playbacks(instance* inst, int64_t page, char* payload, size_t payload_length){ + maweb_instance_data* data = (maweb_instance_data*) inst->impl; size_t base_offset = json_obj_offset(payload, "itemGroups"), group_offset, subgroup_offset, item_offset; uint64_t group = 0, subgroup, item, metatype; @@ -466,8 +466,9 @@ static int maweb_process_playbacks(instance* inst, int64_t page, char* payload, } group++; } - updates_inflight--; - DBGPF("Playback message processing done, %" PRIu64 " updates inflight", updates_inflight); + + data->updates_inflight--; + DBGPF("Playback message processing done, %" PRIu64 " updates inflight on %s", data->updates_inflight, inst->name); return 0; } @@ -479,9 +480,9 @@ static int maweb_request_playbacks(instance* inst){ char item_indices[1024] = "[300,400,500]", item_counts[1024] = "[16,16,16]", item_types[1024] = "[3,3,3]"; size_t page_index = 0, view = 3, channel = 0, offsets[3], channel_offset, channels; - if(updates_inflight){ + if(data->updates_inflight){ if(quiet_mode < 1){ - LOGPF("Skipping update request, %" PRIu64 " updates still inflight - consider raising the interval time", updates_inflight); + LOGPF("Skipping update request on %s, %" PRIu64 " updates still inflight - consider raising the interval time", inst->name, data->updates_inflight); } return 0; } @@ -572,15 +573,16 @@ static int maweb_request_playbacks(instance* inst){ data->session); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); DBGPF("Poll request: %s", xmit_buffer); - updates_inflight++; + data->updates_inflight++; } - DBGPF("Poll request handling done, %" PRIu64 " updates requested", updates_inflight); + DBGPF("Poll request handling done, %" PRIu64 " updates requested on %s", data->updates_inflight, inst->name); return rv; } static int maweb_handle_message(instance* inst, char* payload, size_t payload_length){ char xmit_buffer[MAWEB_XMIT_CHUNK]; + int64_t session = 0; char* field; maweb_instance_data* data = (maweb_instance_data*) inst->impl; @@ -591,6 +593,11 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(json_obj_bool(payload, "result", 0)){ LOG("Login successful"); data->login = 1; + + //initially request playbacks + if(!update_interval){ + maweb_request_playbacks(inst); + } } else{ LOG("Login failed"); @@ -601,21 +608,28 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(maweb_process_playbacks(inst, json_obj_int(payload, "iPage", 0), payload, payload_length)){ LOG("Failed to handle/request input data"); } + + //request playbacks again if configured + if(!update_interval && data->login && !data->updates_inflight){ + maweb_request_playbacks(inst); + } return 0; } } DBGPF("Incoming message (%" PRIsize_t "): %s", payload_length, payload); if(json_obj(payload, "session") == JSON_NUMBER){ - data->session = json_obj_int(payload, "session", data->session); - if(data->session < 0){ + session = json_obj_int(payload, "session", data->session); + if(session < 0){ LOG("Login failed"); + data->session = -1; data->login = 0; return 0; } - if(quiet_mode < 2){ - LOGPF("Session id is now %" PRId64, data->session); + if(data->session != session){ + LOGPF("Session ID changed from %" PRId64 " to %" PRId64 "", data->session, session); } + data->session = session; } if(json_obj_bool(payload, "forceLogin", 0)){ @@ -1047,7 +1061,7 @@ static int maweb_handle(size_t num, managed_fd* fds){ last_keepalive = mm_timestamp(); } - if(last_update && mm_timestamp() - last_update >= update_interval){ + if(update_interval && last_update && mm_timestamp() - last_update >= update_interval){ rv |= maweb_poll(); last_update = mm_timestamp(); } -- cgit v1.2.3 From 51b281340ba319eaa8399d507b00eaefa6c6a469 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 30 May 2020 12:23:32 +0200 Subject: maweb: Try next host (if configured) on login failure --- backends/maweb.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 980a914..297fb5f 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -593,15 +593,24 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(json_obj_bool(payload, "result", 0)){ LOG("Login successful"); data->login = 1; - + //initially request playbacks if(!update_interval){ maweb_request_playbacks(inst); } } else{ - LOG("Login failed"); data->login = 0; + + if(data->hosts > 1){ + LOGPF("Console login failed on %s, will try again with the next host", inst->name); + //mark as closed to reconnect + data->state = ws_closed; + } + else{ + LOGPF("Console login failed on %s", inst->name); + } + return 0; } } if(!strncmp(field, "playbacks", 9)){ @@ -621,10 +630,13 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(json_obj(payload, "session") == JSON_NUMBER){ session = json_obj_int(payload, "session", data->session); if(session < 0){ - LOG("Login failed"); - data->session = -1; - data->login = 0; - return 0; + LOG("Invalid session ID received, closing connection"); + data->session = -1; + data->login = 0; + + //this should be enough to mark the socket for the next keepalive/establish run + data->state = ws_closed; + return 0; } if(data->session != session){ LOGPF("Session ID changed from %" PRId64 " to %" PRId64 "", data->session, session); -- cgit v1.2.3 From 257ce41f43837dc1f1195d6ead397151c309e59b Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 2 Jun 2020 21:28:59 +0200 Subject: Change maweb session identifier message (Fixes #62) --- backends/maweb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 297fb5f..1f8fcf0 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -630,7 +630,7 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(json_obj(payload, "session") == JSON_NUMBER){ session = json_obj_int(payload, "session", data->session); if(session < 0){ - LOG("Invalid session ID received, closing connection"); + LOG("Invalid web remote session identifier received, closing connection"); data->session = -1; data->login = 0; @@ -639,7 +639,7 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le return 0; } if(data->session != session){ - LOGPF("Session ID changed from %" PRId64 " to %" PRId64 "", data->session, session); + LOGPF("Web remote session ID changed from %" PRId64 " to %" PRId64 "", data->session, session); } data->session = session; } -- cgit v1.2.3 From e52c67ce0da7c3847d2583c8df6d5f1bd1fecb9e Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 5 Jun 2020 23:42:57 +0200 Subject: Log out of maweb session on disconnect (Fixes #61) --- backends/maweb.c | 56 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 1f8fcf0..7829ec4 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -16,6 +16,8 @@ #define WS_FLAG_FIN 0x80 #define WS_FLAG_MASK 0x80 +static void maweb_disconnect(instance* inst); + static uint64_t last_keepalive = 0; static uint64_t update_interval = 0; static uint64_t last_update = 0; @@ -351,8 +353,7 @@ static int maweb_send_frame(instance* inst, maweb_operation op, uint8_t* payload if(mmbackend_send(data->fd, frame_header, header_bytes) || mmbackend_send(data->fd, payload, len)){ LOGPF("Failed to send on instance %s, assuming connection failure", inst->name); - data->state = ws_closed; - data->login = 0; + maweb_disconnect(inst); return 1; } @@ -604,8 +605,7 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le if(data->hosts > 1){ LOGPF("Console login failed on %s, will try again with the next host", inst->name); - //mark as closed to reconnect - data->state = ws_closed; + maweb_disconnect(inst); } else{ LOGPF("Console login failed on %s", inst->name); @@ -631,11 +631,7 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le session = json_obj_int(payload, "session", data->session); if(session < 0){ LOG("Invalid web remote session identifier received, closing connection"); - data->session = -1; - data->login = 0; - - //this should be enough to mark the socket for the next keepalive/establish run - data->state = ws_closed; + maweb_disconnect(inst); return 0; } if(data->session != session){ @@ -668,6 +664,30 @@ static int maweb_handle_message(instance* inst, char* payload, size_t payload_le return 0; } +static void maweb_disconnect(instance* inst){ + maweb_instance_data* data = (maweb_instance_data*) inst->impl; + char xmit_buffer[MAWEB_XMIT_CHUNK]; + + if(data->fd){ + //close the session if one is active + if(data->session > 0){ + snprintf(xmit_buffer, sizeof(xmit_buffer), "{\"requestType\":\"close\",\"session\":%" PRIu64 "}", data->session); + maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); + } + + mm_manage_fd(data->fd, BACKEND_NAME, 0, NULL); + close(data->fd); + } + + data->fd = -1; + data->state = ws_closed; + data->login = 0; + data->session = -1; + data->peer_type = peer_unidentified; + data->offset = 0; + data->updates_inflight = 0; +} + static int maweb_connect(instance* inst){ int rv = 1; maweb_instance_data* data = (maweb_instance_data*) inst->impl; @@ -676,14 +696,8 @@ static int maweb_connect(instance* inst){ goto bail; } - //unregister old fd from core - if(data->fd >= 0){ - mm_manage_fd(data->fd, BACKEND_NAME, 0, NULL); - close(data->fd); - data->fd = -1; - } - data->state = ws_closed; - data->login = 0; + //close old connection and reset state + maweb_disconnect(inst); LOGPF("Connecting to host %" PRIsize_t " of %" PRIsize_t " on %s", data->next_host + 1, data->hosts, inst->name); @@ -1140,14 +1154,10 @@ static int maweb_shutdown(size_t n, instance** inst){ free(data->pass); data->pass = NULL; - close(data->fd); - data->fd = -1; - + maweb_disconnect(inst[u]); free(data->buffer); data->buffer = NULL; - - data->offset = data->allocated = 0; - data->state = ws_closed; + data->allocated = 0; free(data->channel); data->channel = NULL; -- cgit v1.2.3 From 5e4c3fe5a81f548243aab01a42973333b559004f Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 11 Jun 2020 12:27:57 +0200 Subject: Query joystick buttons --- backends/maweb.c | 2 +- backends/wininput.c | 87 ++++++++++++++++++++++++++++++++++++++++++++-------- backends/wininput.h | 5 ++- backends/wininput.md | 5 ++- 4 files changed, 84 insertions(+), 15 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 7829ec4..33d2b7e 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -669,7 +669,7 @@ static void maweb_disconnect(instance* inst){ char xmit_buffer[MAWEB_XMIT_CHUNK]; if(data->fd){ - //close the session if one is active + //close the session if one is active if(data->session > 0){ snprintf(xmit_buffer, sizeof(xmit_buffer), "{\"requestType\":\"close\",\"session\":%" PRIu64 "}", data->session); maweb_send_frame(inst, ws_text, (uint8_t*) xmit_buffer, strlen(xmit_buffer)); diff --git a/backends/wininput.c b/backends/wininput.c index 71e9855..627891e 100644 --- a/backends/wininput.c +++ b/backends/wininput.c @@ -1,5 +1,5 @@ #define BACKEND_NAME "wininput" -#define DEBUG +//#define DEBUG #include #include "wininput.h" @@ -7,6 +7,7 @@ #include //TODO check whether feedback elimination is required +//TODO might want to store virtual desktop extents in request->limit static key_info keys[] = { {VK_LBUTTON, "lmb", button}, {VK_RBUTTON, "rmb", button}, {VK_MBUTTON, "mmb", button}, @@ -57,6 +58,17 @@ static key_info keys[] = { {VK_ZOOM, "zoom"} }; +//this monstrosity is necessary because not only are the buttons not a simple bitmask, the bits are also partially reused. +//i get why they replaced this heap of trash API, but the replacement would require me to jump through even more hoops. +static uint32_t button_masks[32] = {JOY_BUTTON1, JOY_BUTTON2, JOY_BUTTON3, JOY_BUTTON4, + JOY_BUTTON5, JOY_BUTTON6, JOY_BUTTON7, JOY_BUTTON8, + JOY_BUTTON9, JOY_BUTTON10, JOY_BUTTON11, JOY_BUTTON12, + JOY_BUTTON13, JOY_BUTTON14, JOY_BUTTON15, JOY_BUTTON16, + JOY_BUTTON17, JOY_BUTTON18, JOY_BUTTON19, JOY_BUTTON20, + JOY_BUTTON21, JOY_BUTTON22, JOY_BUTTON23, JOY_BUTTON24, + JOY_BUTTON25, JOY_BUTTON26, JOY_BUTTON27, JOY_BUTTON28, + JOY_BUTTON29, JOY_BUTTON30, JOY_BUTTON31, JOY_BUTTON32}; + static struct { int virtual_x, virtual_y, virtual_width, virtual_height; long mouse_x, mouse_y; @@ -159,7 +171,7 @@ static int wininput_subscribe(uint64_t ident, channel* chan){ cfg.request[u].ident.label = ident; cfg.request[u].channels = 0; cfg.request[u].channel = NULL; - cfg.request[u].state = 0; + cfg.request[u].state = cfg.request[u].min = cfg.request[u].max = 0; cfg.requests++; } @@ -276,7 +288,7 @@ static uint64_t wininput_channel_joystick(instance* inst, char* spec, uint8_t fl if(strlen(token) == 1 || !strcmp(token, "pov")){ if(strchr(axes, token[0])){ ident.fields.channel = position; - ident.fields.control = (controller << 8) | token[0]; + ident.fields.control = ((controller - 1) << 8) | token[0]; return ident.label; } @@ -511,13 +523,25 @@ static int wininput_handle(size_t num, managed_fd* fds){ if(cfg.request[u].ident.fields.channel == button){ //button query if(joy_info.dwFlags & JOY_RETURNBUTTONS){ - //TODO handle button requests + key_state = (joy_info.dwButtons & button_masks[(cfg.request[u].ident.fields.control & 0xFF)]) > 0 ? 1 : 0; + if(key_state != cfg.request[u].state){ + if(key_state){ + val.normalised = 1.0; + } + cfg.request[u].state = key_state; + push_event = 1; + DBGPF("Joystick %d button %d: %d", + cfg.request[u].ident.fields.control >> 8, + cfg.request[u].ident.fields.control & 0xFF, + key_state); + } } else{ LOGPF("No button data received for joystick %d", cfg.request[u].ident.fields.control >> 8); } } else{ + //TODO handle axis requests } } @@ -542,17 +566,11 @@ static int wininput_handle(size_t num, managed_fd* fds){ return 0; } -static int wininput_start(size_t n, instance** inst){ - size_t u; - POINT cursor_position; +static void wininput_start_joystick(){ + size_t u, p; JOYINFOEX joy_info; JOYCAPS joy_caps; - //if no input requested, don't request polling - if(!cfg.requests){ - cfg.interval = 0; - } - DBGPF("This system supports a maximum of %u joysticks", joyGetNumDevs()); for(u = 0; u < joyGetNumDevs(); u++){ joy_info.dwSize = sizeof(joy_info); @@ -560,12 +578,57 @@ static int wininput_start(size_t n, instance** inst){ if(joyGetPosEx(u, &joy_info) == JOYERR_NOERROR){ if(joyGetDevCaps(u, &joy_caps, sizeof(joy_caps)) == JOYERR_NOERROR){ LOGPF("Joystick %" PRIsize_t " (%s) is available for input", u + 1, joy_caps.szPname ? joy_caps.szPname : "unknown model"); + for(p = 0; p < cfg.requests; p++){ + if(cfg.request[p].ident.fields.type == joystick + && cfg.request[p].ident.fields.channel == position + && (cfg.request[p].ident.fields.control >> 8) == u){ + //this looks really dumb, but the structure is defined in a way that prevents us from doing anything clever here + switch(cfg.request[p].ident.fields.control & 0xFF){ + case 'x': + cfg.request[p].min = joy_caps.wXmin; + cfg.request[p].max = joy_caps.wXmax; + break; + case 'y': + cfg.request[p].min = joy_caps.wYmin; + cfg.request[p].max = joy_caps.wYmax; + break; + case 'z': + cfg.request[p].min = joy_caps.wZmin; + cfg.request[p].max = joy_caps.wZmax; + break; + case 'r': + cfg.request[p].min = joy_caps.wRmin; + cfg.request[p].max = joy_caps.wRmax; + break; + case 'u': + cfg.request[p].min = joy_caps.wUmin; + cfg.request[p].max = joy_caps.wUmax; + break; + case 'v': + cfg.request[p].min = joy_caps.wVmin; + cfg.request[p].max = joy_caps.wVmax; + break; + } + DBGPF("Updated limits on request %" PRIsize_t " to %" PRIu32 " / %" PRIu32, p, cfg.request[p].min, cfg.request[p].max); + } + } } else{ LOGPF("Joystick %" PRIsize_t " available for input, but no capabilities reported", u + 1); } } } +} + +static int wininput_start(size_t n, instance** inst){ + POINT cursor_position; + + //if no input requested, don't request polling + if(!cfg.requests){ + cfg.interval = 0; + } + + wininput_start_joystick(); //read virtual desktop extents for later normalization cfg.virtual_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); diff --git a/backends/wininput.h b/backends/wininput.h index 0318724..6ec9f46 100644 --- a/backends/wininput.h +++ b/backends/wininput.h @@ -47,5 +47,8 @@ typedef struct /*_input_request*/ { wininput_channel_ident ident; size_t channels; channel** channel; - uint16_t state; + uint32_t state; + + //used for jostick axes + uint32_t min, max; } wininput_request; diff --git a/backends/wininput.md b/backends/wininput.md index 6ead158..6e665bb 100644 --- a/backends/wininput.md +++ b/backends/wininput.md @@ -79,8 +79,11 @@ input.X > wi1.key.escape #### Known bugs / problems +Joysticks can only be used as input to the MIDIMonster, as Windows does not provide a method to emulate +Joystick input from user space. This is unlikely to change. + Keyboard and mouse input is subject to UIPI. You can not send input to applications that run at a higher -privilege level than the MIDIMonster. +privilege level than the MIDIMonster. This limitation is by design and will not change. Due to inconsistencies in the Windows API, mouse position input and output may differ for the same cursor location. This may be correlated with the use and arrangement of multi-monitor desktops. If you encounter problems with either -- cgit v1.2.3 From 7f2f36ddaa9c8311404248c69cc686bac6264ceb Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 1 Aug 2020 20:04:25 +0200 Subject: Allow initial maweb event push even for zero values (#74) --- backends/maweb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 33d2b7e..39ef7a6 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -249,7 +249,7 @@ static int maweb_instance(instance* inst){ static channel* maweb_channel(instance* inst, char* spec, uint8_t flags){ maweb_instance_data* data = (maweb_instance_data*) inst->impl; maweb_channel_data chan = { - 0 + .in = -1 //this hack allows the initial data request to push events even for zero'ed channels }; char* next_token = NULL; channel* channel_ref = NULL; -- cgit v1.2.3 From ca055614db57386f0e43d65bb10e501bdde699ef Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 22 Jun 2021 03:29:08 +0200 Subject: Keep channel registry map in sync when updating identifiers (#76) --- backends/maweb.c | 2 +- core/backend.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- core/backend.h | 1 + midimonster.c | 4 ++-- midimonster.h | 25 ++++++++++++++++++++----- 5 files changed, 69 insertions(+), 10 deletions(-) (limited to 'backends/maweb.c') diff --git a/backends/maweb.c b/backends/maweb.c index 39ef7a6..8b878b0 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -1111,7 +1111,7 @@ static int maweb_start(size_t n, instance** inst){ //re-set channel identifiers for(p = 0; p < data->channels; p++){ - data->channel[p].chan->ident = p; + mm_channel_update(data->channel[p].chan, p); } //try to connect to any available host diff --git a/core/backend.c b/core/backend.c index 16e095c..8a8588f 100644 --- a/core/backend.c +++ b/core/backend.c @@ -94,9 +94,9 @@ int backends_notify(size_t nev, channel** c, channel_value* v){ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ size_t u, bucket = channelstore_hash(inst, ident); - DBGPF("\tSearching for inst %" PRIu64 " ident %" PRIu64, inst, ident); + DBGPF("\tSearching for inst %" PRIu64 " ident %" PRIu64, (uint64_t) inst, ident); for(u = 0; u < channels.n[bucket]; u++){ - DBGPF("\tBucket %" PRIsize_t " entry %" PRIsize_t " inst %" PRIu64 " ident %" PRIu64, bucket, u, channels.entry[bucket][u]->instance, channels.entry[bucket][u]->ident); + DBGPF("\tBucket %" PRIsize_t " entry %" PRIsize_t " inst %" PRIu64 " ident %" PRIu64, bucket, u, (uint64_t) channels.entry[bucket][u]->instance, channels.entry[bucket][u]->ident); if(channels.entry[bucket][u]->instance == inst && channels.entry[bucket][u]->ident == ident){ DBGPF("Requested channel %" PRIu64 " on instance %s already exists, reusing (bucket %" PRIsize_t ", %" PRIsize_t " search steps)\n", ident, inst->name, bucket, u); @@ -128,6 +128,49 @@ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ return channels.entry[bucket][(channels.n[bucket]++)]; } +MM_API void mm_channel_update(channel* chan, uint64_t ident){ + size_t bucket = channelstore_hash(chan->instance, chan->ident), new_bucket = channelstore_hash(chan->instance, ident); + size_t u; + + DBGPF("Updating identifier for inst %" PRIu64 " ident %" PRIu64 " (bucket %" PRIsize_t " to %" PRIsize_t ") to %" PRIu64, (uint64_t) chan->instance, chan->ident, bucket, new_bucket, ident); + + if(bucket == new_bucket){ + chan->ident = ident; + return; + } + + for(u = 0; u < channels.n[bucket]; u++){ + if(channels.entry[bucket][u]->instance == chan->instance + && channels.entry[bucket][u]->ident == chan->ident){ + break; + } + } + + if(u == channels.n[bucket]){ + DBGPF("Failed to find channel to update in bucket %" PRIsize_t, bucket); + return; + } + + DBGPF("Removing channel from slot %" PRIsize_t " of %" PRIsize_t " of bucket %" PRIsize_t, u, channels.n[bucket], bucket); + //remove channel from old bucket + for(; u < channels.n[bucket] - 1; u++){ + channels.entry[bucket][u] = channels.entry[bucket][u + 1]; + } + + //add to new bucket + channels.entry[new_bucket] = realloc(channels.entry[new_bucket], (channels.n[new_bucket] + 1) * sizeof(channel*)); + if(!channels.entry[new_bucket]){ + fprintf(stderr, "Failed to allocate memory\n"); + channels.n[new_bucket] = 0; + return; + } + + channels.entry[new_bucket][channels.n[new_bucket]] = chan; + chan->ident = ident; + channels.n[bucket]--; + channels.n[new_bucket]++; +} + instance* mm_instance(backend* b){ size_t u = 0, n = 0; diff --git a/core/backend.h b/core/backend.h index 6a69508..46c6c3a 100644 --- a/core/backend.h +++ b/core/backend.h @@ -12,6 +12,7 @@ instance* mm_instance(backend* b); /* Backend API */ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create); +MM_API void mm_channel_update(channel* chan, uint64_t ident); MM_API instance* mm_instance_find(char* name, uint64_t ident); MM_API int mm_backend_instances(char* name, size_t* ninst, instance*** inst); MM_API int mm_backend_register(backend b); diff --git a/midimonster.c b/midimonster.c index 51fe7ad..5817ac7 100644 --- a/midimonster.c +++ b/midimonster.c @@ -346,7 +346,7 @@ static int core_process(size_t nfds, managed_fd* signaled_fds){ size_t u, swaps = 0; //run backend processing, collect events - DBGPF("%lu backend FDs signaled\n", nfds); + DBGPF("%lu backend FDs signaled", nfds); if(backends_handle(nfds, signaled_fds)){ return 1; } @@ -354,7 +354,7 @@ static int core_process(size_t nfds, managed_fd* signaled_fds){ //limit number of collector swaps per iteration to prevent complete deadlock while(routing.events->n && swaps < MM_SWAP_LIMIT){ //swap primary and secondary event collectors - DBGPF("Swapping event collectors, %lu events in primary\n", routing.events->n); + DBGPF("Swapping event collectors, %lu events in primary", routing.events->n); for(u = 0; u < sizeof(routing.pool) / sizeof(routing.pool[0]); u++){ if(routing.events != routing.pool + u){ secondary = routing.events; diff --git a/midimonster.h b/midimonster.h index 9552b7e..89688c4 100644 --- a/midimonster.h +++ b/midimonster.h @@ -227,15 +227,21 @@ MM_API int mm_backend_register(backend b); MM_API instance* mm_instance_find(char* backend, uint64_t ident); /* - * Provides a pointer to a channel structure, pre-filled with the provided - * instance reference and identifier. + * This function is the main interface to the core-provided channel registry. + * This API is just a convenience function. Creating and managing a + * backend-internal channel store is possible (and encouraged for performance + * reasons). + * + * Channels are identified by the (instance, ident) tuple within the registry. + * + * This API provides a pointer to a channel structure, pre-filled with the + * provided instance reference and identifier. * The `create` parameter is a boolean flag indicating whether a channel * matching the `ident` parameter should be created in the global channel store * if none exists yet. If the instance already registered a channel matching * `ident`, a pointer to the existing channel is returned. - * This API is just a convenience function. Creating and managing a - * backend-internal channel store is possible (and encouraged for performance - * reasons). When returning pointers from a backend-local channel store, the + * + * When returning pointers from a backend-local channel store, the * returned pointers must stay valid over the lifetime of the instance and * provide valid `instance` members, as they are used for callbacks. * For each channel with a non-NULL `impl` field registered using @@ -244,6 +250,15 @@ MM_API instance* mm_instance_find(char* backend, uint64_t ident); */ MM_API channel* mm_channel(instance* i, uint64_t ident, uint8_t create); +/* + * When using the core-provided channel registry, the identification + * member of the structure must only be updated using this API. + * The tuple of (instance, ident) is used as key to the backing + * storage of the channel registry, thus the registry must be notified + * of changes. + */ +MM_API void mm_channel_update(channel* c, uint64_t ident); + /* * Register (manage = 1) or unregister (manage = 0) a file descriptor to be * selected on. The backend will be notified when the descriptor becomes ready -- cgit v1.2.3