From 9319f162bcbc668547aba228402d3d13d770914f Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Sun, 26 Mar 2023 22:13:17 +0200 Subject: [PATCH] vnc: decouple monitor data from app, for no particular reason I have some suspicion MONITOR_COUNT betrayed me. --- vnc-client/src/main.c | 311 +++++++++++++++++++++++++----------------- 1 file changed, 183 insertions(+), 128 deletions(-) diff --git a/vnc-client/src/main.c b/vnc-client/src/main.c index 26d60cc..56cf358 100644 --- a/vnc-client/src/main.c +++ b/vnc-client/src/main.c @@ -10,24 +10,33 @@ /* Size of the calculator display */ #define CALC_WIDTH 396 #define CALC_HEIGHT 224 +/* Number of monitors */ +#define MONITOR_COUNT 2 -/* Application globals */ -struct app { +/* Tracking data for a calculator-attached virtual monitor */ +struct monitor { /* RFB/VNC client to get framebuffers from VNC server */ rfbClient *client; /* SDL globals used to store the framebuffer and show it on-screen */ SDL_Window *window; SDL_Surface *surface; + /* Calculator */ + struct fxlink_device *calc; + libusb_device *calc_unique_id; + /* 16-bit framebuffer for the calculator (big endian) */ + uint16_t *fb16_be; +}; + +/* Application globals */ +struct app { /* CLI options */ bool display_calc; bool display_sdl; /* Calculator tracking */ libusb_context *libusb_ctx; struct fxlink_device_list devices; - struct fxlink_device *calc; - libusb_device *calc_unique_id; - /* 16-bit framebuffer for the calculator (big endian) */ - uint16_t *fb16_be; + /* All monitors */ + struct monitor *monitors[MONITOR_COUNT]; }; static struct app app = { 0 }; @@ -38,19 +47,25 @@ static void cleanup(void) while(fxlink_device_list_interrupt(&app.devices)) libusb_handle_events(app.libusb_ctx); - if(app.client) - rfbClientCleanup(app.client); + for(int i = 0; i < MONITOR_COUNT; i++) { + struct monitor *mon = app.monitors[i]; + if(!mon) + continue; + + if(mon->client) + rfbClientCleanup(mon->client); + if(mon->window) + SDL_DestroyWindow(mon->window); + + /* This device is managed by the device list */ + mon->calc = NULL; + mon->calc_unique_id = NULL; + + free(mon->fb16_be); + free(mon); + } - if(app.window) - SDL_DestroyWindow(app.window); SDL_Quit(); - - /* This device is managed by the device list */ - app.calc = NULL; - app.calc_unique_id = NULL; - - free(app.fb16_be); - fxlink_device_list_stop(&app.devices); if(app.libusb_ctx) libusb_exit(app.libusb_ctx); @@ -61,14 +76,15 @@ static void cleanup(void) static void fb_update(rfbClient *client) { uint32_t *fb = (void *)client->frameBuffer; + struct monitor *mon = rfbClientGetClientData(client, NULL); if(app.display_sdl) { /* Very crude assumption about the SDL surface format and pitch */ - memcpy(app.surface->pixels, fb, CALC_WIDTH * CALC_HEIGHT * 4); - SDL_UpdateWindowSurface(app.window); + memcpy(mon->surface->pixels, fb, CALC_WIDTH * CALC_HEIGHT * 4); + SDL_UpdateWindowSurface(mon->window); } - if(app.display_calc && app.calc && app.fb16_be) { + if(app.display_calc && mon->calc && mon->fb16_be) { for(int y = 0; y < CALC_HEIGHT; y++) for(int x = 0; x < CALC_WIDTH; x++) { uint32_t color = fb[CALC_WIDTH * y + x]; @@ -78,11 +94,11 @@ static void fb_update(rfbClient *client) /* Conversion to RGB565 */ int c = ((R & 0xf8) << 8) | ((G & 0xfc) << 3) | ((B & 0xf8) >> 3); - app.fb16_be[CALC_WIDTH * y + x] = (c >> 8) | (c << 8); + mon->fb16_be[CALC_WIDTH * y + x] = (c >> 8) | (c << 8); } - fxlink_device_start_bulk_OUT(app.calc, - "cgvm", "fb", app.fb16_be, CALC_WIDTH * CALC_HEIGHT * 2, false); + fxlink_device_start_bulk_OUT(mon->calc, + "cgvm", "fb", mon->fb16_be, CALC_WIDTH * CALC_HEIGHT * 2, false); } } @@ -156,13 +172,18 @@ static rfbKeySym keycode_to_rfbKeySym(int keycode) /* Handle incoming messages from the calculator. */ static void handle_calc_message(struct fxlink_message const *msg) { + /* Send messages to the first VNC server around */ + struct monitor *mon = NULL; + for(int i = 0; i < MONITOR_COUNT && !mon; i++) + mon = app.monitors[i]; + if(fxlink_message_is_apptype(msg, "cgvm", "pressed-keys")) { uint8_t *keys = msg->data; for(int i = 0; i < msg->size / 2; i++) { int code = keycode_to_rfbKeySym(keys[2*i]); int down = keys[2*i+1] ? TRUE : FALSE; - if(code > 0) - SendKeyEvent(app.client, code, down); + if(code > 0 && mon) + SendKeyEvent(mon->client, code, down); } } else { @@ -182,38 +203,120 @@ static void usage(int rc) exit(rc); } -static bool init_rfb_client(rfbClient **client, char *server, - uint32_t *fb) +static struct monitor *monitor_create(char *server) { + struct monitor *mon = calloc(1, sizeof *mon); + assert(mon && "out of memory"); + + /** Configure client **/ + + rfbClient *client = rfbGetClient(8, 3, 4); + if(!client) { + fprintf(stderr, "rfbGetClient failed\n"); + return NULL; + } + rfbClientSetClientData(client, NULL, mon); + + client->FinishedFrameBufferUpdate = fb_update; + client->width = CALC_WIDTH; + client->height = CALC_HEIGHT; + client->frameBuffer = malloc(CALC_WIDTH * CALC_HEIGHT * 4); + assert(client->frameBuffer && "out of memory"); + + /* Standard 32-bit xRGB */ + client->format.bitsPerPixel = 32; + client->format.redShift = 16; + client->format.greenShift = 8; + client->format.blueShift = 0; + client->format.redMax = 0xff; + client->format.greenMax = 0xff; + client->format.blueMax = 0xff; + SetFormatAndEncodings(client); + + /** Connect to VNC server **/ + int argc = 4; char *argv[] = { "cgvm_vnc", "-encodings", "raw", server, NULL }; - *client = rfbGetClient(8, 3, 4); - if(!*client) { - fprintf(stderr, "rfbGetClient failed\n"); - return false; - } - (*client)->FinishedFrameBufferUpdate = fb_update; - (*client)->width = CALC_WIDTH; - (*client)->height = CALC_HEIGHT; - (*client)->frameBuffer = (void *)fb; - - /* Standard 32-bit xRGB */ - (*client)->format.bitsPerPixel = 32; - (*client)->format.redShift = 16; - (*client)->format.greenShift = 8; - (*client)->format.blueShift = 0; - (*client)->format.redMax = 0xff; - (*client)->format.greenMax = 0xff; - (*client)->format.blueMax = 0xff; - SetFormatAndEncodings(*client); - - if(!rfbInitClient(*client, &argc, argv)) { + if(!rfbInitClient(client, &argc, argv)) { fprintf(stderr, "rfbInitClient failed\n"); - *client = NULL; - return false; + return NULL; + } + mon->client = client; + + /** Create the SDL window if SDL display is requested **/ + + if(app.display_sdl) { + if(!SDL_WasInit(SDL_INIT_VIDEO)) + SDL_Init(SDL_INIT_VIDEO); + + mon->window = SDL_CreateWindow("CG Virtual Monitor", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + CALC_WIDTH, CALC_HEIGHT, 0); + mon->surface = SDL_GetWindowSurface(mon->window); + + /* Surely this is gonna be the default everywhere, like surely */ + assert(mon->surface->format->BytesPerPixel == 4); + assert(mon->surface->format->format == SDL_PIXELFORMAT_RGB888); + } + + /** Create calculator framebuffer and stay ready to connect anytime */ + + mon->fb16_be = malloc(CALC_WIDTH * CALC_HEIGHT * 2); + assert(mon->fb16_be && "out of memory"); + + return mon; +} + +void monitor_update(struct monitor *mon) +{ + /* Check if the calculator disconnected */ + if(mon->calc) { + bool still_here = false; + for(int i = 0; i < app.devices.count; i++) { + still_here |= app.devices.devices[i].dp == mon->calc_unique_id; + } + if(!still_here) { + hlog("cgvm"); + log_("calculator disconnected!\n"); + mon->calc = NULL; + mon->calc_unique_id = NULL; + } + } + + /* Check for devices ready to connect to */ + if(!mon->calc) { + for(int i = 0; i < app.devices.count; i++) { + struct fxlink_device *fdev = &app.devices.devices[i]; + char const *id = fxlink_device_id(fdev); + + if(fdev->status != FXLINK_FDEV_STATUS_IDLE || !fdev->comm) + continue; + if(fdev->comm->ep_bulk_IN == 0xff) { + hlog("cgvm"); + log_("ignoring %s: no fxlink interface\n", id); + continue; + } + if(!fxlink_device_claim_fxlink(fdev)) + continue; + + hlog("cgvm"); + log_("starting virtual monitor on %s\n", id); + mon->calc = fdev; + mon->calc_unique_id = fdev->dp; + fxlink_device_start_bulk_IN(fdev); + } + } + + /* Handle incoming transfers from the calc */ + if(mon->calc) { + struct fxlink_message *msg = fxlink_device_finish_bulk_IN(mon->calc); + if(msg) { + handle_calc_message(msg); + fxlink_message_free(msg, true); + fxlink_device_start_bulk_IN(mon->calc); + } } - return true; } int main(int argc, char **argv) @@ -240,28 +343,7 @@ int main(int argc, char **argv) server loops and can't be killed by SIGINT or SIGTERM? */ signal(SIGINT, exit); - //--- - // Initialise the RFB client - //--- - - uint32_t *fb = malloc(CALC_WIDTH * CALC_HEIGHT * 4); - assert(fb && "out of memory"); - - if(!init_rfb_client(&app.client, "127.0.0.1", fb)) - return 1; - - /* Create the SDL window if SDL display is requested */ - if(app.display_sdl) { - SDL_Init(SDL_INIT_VIDEO); - app.window = SDL_CreateWindow("CG Virtual Monitor test VNC client", - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - CALC_WIDTH, CALC_HEIGHT, 0); - app.surface = SDL_GetWindowSurface(app.window); - - /* Surely this is gonna be the default everywhere, like surely */ - assert(app.surface->format->BytesPerPixel == 4); - assert(app.surface->format->format == SDL_PIXELFORMAT_RGB888); - } + /** Initialize libusb to find calculators **/ if(app.display_calc) { int rc; @@ -274,9 +356,14 @@ int main(int argc, char **argv) /* Track the list of connected calculators. */ fxlink_device_list_track(&app.devices, app.libusb_ctx); - app.fb16_be = malloc(CALC_WIDTH * CALC_HEIGHT * 2); } + /** Initialize monitors **/ + + app.monitors[0] = monitor_create("127.0.0.1"); + if(!app.monitors[0]) + return 1; + while(1) { if(app.display_sdl) { SDL_Event e; @@ -288,14 +375,22 @@ int main(int argc, char **argv) } } - int i = WaitForMessage(app.client, 100); - if(i < 0) { - fprintf(stderr, "WaitForMessage() select: %d\n", i); - continue; - } - else if(i > 0 && !HandleRFBServerMessage(app.client)) { - fprintf(stderr, "HandleRFBServerMessage() failed\n"); - continue; + /** Process messages from all monitors **/ + + for(int i = 0; i < MONITOR_COUNT; i++) { + struct monitor *mon = app.monitors[i]; + if(!mon) + continue; + + int rc = WaitForMessage(mon->client, 1000); + if(rc < 0) { + fprintf(stderr, "WaitForMessage() select: %d\n", rc); + continue; + } + if(rc > 0 && !HandleRFBServerMessage(mon->client)) { + fprintf(stderr, "HandleRFBServerMessage() failed\n"); + continue; + } } /* Run libusb's event loop */ @@ -303,53 +398,13 @@ int main(int argc, char **argv) libusb_handle_events_timeout(app.libusb_ctx, &zero_tv); fxlink_device_list_refresh(&app.devices); - /* Check if the calculator disconnected */ - if(app.calc) { - bool still_here = false; - for(int i = 0; i < app.devices.count; i++) { - still_here |= app.devices.devices[i].dp == app.calc_unique_id; - } - if(!still_here) { - hlog("cgvm"); - log_("calculator disconnected!\n"); - app.calc = NULL; - app.calc_unique_id = NULL; - } - } - - /* Check for devices ready to connect to */ - if(!app.calc) { - for(int i = 0; i < app.devices.count; i++) { - struct fxlink_device *fdev = &app.devices.devices[i]; - char const *id = fxlink_device_id(fdev); - - if(fdev->status != FXLINK_FDEV_STATUS_IDLE || !fdev->comm) - continue; - if(fdev->comm->ep_bulk_IN == 0xff) { - hlog("cgvm"); - log_("ignoring %s: no fxlink interface\n", id); - continue; - } - if(!fxlink_device_claim_fxlink(fdev)) - continue; - - hlog("cgvm"); - log_("starting virtual monitor on %s\n", id); - app.calc = fdev; - app.calc_unique_id = fdev->dp; - fxlink_device_start_bulk_IN(fdev); - } - } - - /* Handle incoming transfers from the calc */ - if(app.calc) { - struct fxlink_message *msg = fxlink_device_finish_bulk_IN(app.calc); - if(msg) { - handle_calc_message(msg); - fxlink_message_free(msg, true); - fxlink_device_start_bulk_IN(app.calc); - } + /* Update monitors so they can attach to calculators */ + for(int i = 0; i < MONITOR_COUNT; i++) { + struct monitor *mon = app.monitors[i]; + if(mon) + monitor_update(mon); } } + return 0; }