vnc: decouple monitor data from app, for no particular reason
I have some suspicion MONITOR_COUNT betrayed me.
This commit is contained in:
parent
74a7f05e9d
commit
9319f162bc
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue