vnc: decouple monitor data from app, for no particular reason

I have some suspicion MONITOR_COUNT betrayed me.
This commit is contained in:
Lephenixnoir 2023-03-26 22:13:17 +02:00
parent 74a7f05e9d
commit 9319f162bc
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
1 changed files with 183 additions and 128 deletions

View File

@ -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;
}