cg-virtual-monitor/cgvm-addin/src/main.c

152 lines
3.7 KiB
C

#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>
#include <gint/gint.h>
/* Debug mode (uses gint perf counters not committed to repository) */
// #define DEBUG
#ifdef DEBUG
#include <libprof.h>
#include <fxlibc/printf.h>
#endif
//---
// Receiving and displaying video frames
//---
static void process_usb_message(struct usb_fxlink_header const *header)
{
static int count = 0;
count++;
if(header->size > DWIDTH * DHEIGHT * 2) {
dclear(C_BLACK);
dprint(1, 1, C_WHITE, "[%d] %.16s %.16s (%d B)", count,
header->application, header->type, header->size);
dupdate();
return;
}
#ifdef DEBUG
extern prof_t prof_round, prof_core;
prof_round = prof_make();
prof_core = prof_make();
prof_t prof_read = prof_make();
prof_enter(prof_read);
#endif
/* Read the message payload directly into VRAM. We set a timeout so the
calculator doesn't freeze if the communication stops midway because the
VNC client on the PC was interrupted. */
timeout_t tm = timeout_make_ms(250);
usb_read_sync_timeout(usb_ff_bulk_input(), gint_vram, header->size, false,
&tm);
#ifdef DEBUG
prof_leave(prof_read);
dprint_opt(1, 1, C_WHITE, C_BLACK, DTEXT_LEFT, DTEXT_TOP,
"%d ms (read %.1D ms, wait %.1D ms)\n",
prof_time(prof_read) / 1000,
prof_time(prof_round) / 100,
prof_time(prof_core) / 100);
#endif
dupdate();
}
//---
// Sending keyboard events
//---
#define MAX_EVENTS_PER_MESSAGE 16
struct key_event { uint8_t key; bool down; };
/* Keyboard data to be sent to the PC whenever a key is pressed */
static struct key_event keyinfo[MAX_EVENTS_PER_MESSAGE];
/* Whether there is such a transfer in progress */
static bool keyinfo_frame = false;
static void send_keyboard_events_callback(void)
{
keyinfo_frame = false;
}
static void send_keyboard_events(void)
{
int i = 0;
key_event_t ev;
while((ev = pollevent()).type != KEYEV_NONE) {
/* Filter out repeats and any events past the maximum number */
if(ev.type != KEYEV_DOWN && ev.type != KEYEV_UP)
continue;
if(i >= MAX_EVENTS_PER_MESSAGE)
continue;
keyinfo[i].key = ev.key;
keyinfo[i].down = (ev.type == KEYEV_DOWN);
i++;
}
/* Send keyboard updates via fxlink */
if(i > 0 && usb_is_open() && !keyinfo_frame) {
usb_fxlink_header_t header;
usb_fxlink_fill_header(&header, "cgvm", "pressed-keys", 2*i);
/* We don't need to use async writes because all of this clearly fits
in a single buffer (2 kB) and only the commit communicates */
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, &header, sizeof header, false);
usb_write_sync(pipe, keyinfo, i * sizeof *keyinfo, false);
usb_commit_async(pipe, GINT_CALL(send_keyboard_events_callback));
/* Remember that a transfer is in progress */
keyinfo_frame = true;
}
}
//--
// Main loop
//---
int main(void)
{
#ifdef DEBUG
prof_init();
__printf_enable_fixed();
#endif
dclear(C_WHITE);
dprint(1, 1, C_BLACK, "Opening USB connection...");
dupdate();
usb_interface_t const *intf[] = { &usb_ff_bulk, NULL };
usb_open(intf, GINT_CALL_NULL);
usb_open_wait();
dclear(C_WHITE);
dprint(1, 1, C_BLACK, "USB connected!");
dupdate();
struct usb_fxlink_header header;
while(1) {
send_keyboard_events();
if(keydown(KEY_MENU))
gint_osmenu();
if(keydown(KEY_EXIT))
break;
while(usb_fxlink_handle_messages(&header)) {
process_usb_message(&header);
}
}
#ifdef DEBUG
prof_quit();
#endif
return 0;
}