#include #include #include #include #include /* Debug mode (uses gint perf counters not committed to repository) */ // #define DEBUG #ifdef DEBUG #include #include #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; }