keyboard now processes keys via rfb. framework in place for multiple modules.
[dcpu16] / vm-dcpu16.c
index 367cdbc2669f81008edd4443fa4e4e39df751417..a463f7ac94858856c5a6e6a2697f44b44811f44f 100644 (file)
 #include <sys/time.h>
 
 #include <readline/readline.h>
+#ifdef HAVE_LIBVNCSERVER
+#include "rfb/rfb.h"
+#endif /* HAVE_LIBVNCSERVER */
 
 #include "dcpu16.h"
 #include "common.h"
 
 #include "hw_lem1802.h"
+#include "hw_keyboard.h"
 
 /*
  *  shell-like driver for dcpu16 core
@@ -145,6 +149,62 @@ int file_load_(struct dcpu16 *vm, char *filename, DCPU16_WORD addr) {
 }
 
 
+#ifdef HAVE_LIBVNCSERVER
+static struct dynamic_array rfbScreens_;
+/* wups, kbdAddEvent isn't null by default, so I guess track things externally */
+struct rfb_instance_ {
+    rfbScreenInfoPtr screen;
+    struct dcpu16_hw *attached_display;
+    struct dcpu16_hw *attached_keyboard;
+};
+
+/* locate or allocate the next display with an un-occupied framebuffer */
+static
+struct rfb_instance_ *rfbScreen_next_available_display_(struct dynamic_array *rfbScreens, int argc, char *argv[]) {
+    size_t i;
+    struct rfb_instance_ new_instance, *s;
+
+    fprintf(stderr, "DEBUG: rfbScreens->entries:%zu\n", rfbScreens->entries);
+
+    for (i = 0; i < rfbScreens->entries; i++) {
+        s = (struct rfb_instance_ *)DYNARRAY_ITEM(*rfbScreens, i);
+        if (s->attached_display == NULL)
+            return s;
+    }
+
+    new_instance.screen = lem1802_rfb_new(argc, argv);
+    new_instance.attached_display = NULL;
+    new_instance.attached_keyboard = NULL;
+    s = dynarray_add(rfbScreens, &new_instance);
+    return s;
+}
+
+/* locate or allocate the next display with an un-occupied keyboard */
+static
+struct rfb_instance_ *rfbScreen_next_available_keyboard_(struct dynamic_array *rfbScreens, int argc, char *argv[]) {
+    size_t i;
+    struct rfb_instance_ new_instance, *s;
+
+    for (i = 0; i < rfbScreens->entries; i++) {
+        s = (struct rfb_instance_ *)DYNARRAY_ITEM(*rfbScreens, i);
+        if (s->attached_keyboard == NULL)
+            return s;
+    }
+
+    new_instance.screen = lem1802_rfb_new(argc, argv);
+    new_instance.attached_display = NULL;
+    new_instance.attached_keyboard = NULL;
+    s = dynarray_add(rfbScreens, &new_instance);
+    return s;
+}
+
+/* begin serving a screen */
+void rfbScreen_start(rfbScreenInfoPtr s) {
+    rfbInitServer(s);
+    rfbRunEventLoop(s, -1, TRUE);
+}
+#endif /* HAVE_LIBVNCSERVER */
+
 /*
     Here follows the various commands the shell can execute.
 
@@ -380,7 +440,8 @@ COMMAND_IMPL(run) {
     long long run_cycle_start;
     struct timeval start_tv, now_tv, diff_tv;
     long long cycle_start, cycles_to_wait;
-    struct timespec sleep_time;
+    struct timespec sleep_time, rem_time;
+    long long run_usec;
 
     (void)arg_count, (void)arg_vector;
 
@@ -397,7 +458,7 @@ COMMAND_IMPL(run) {
         return -1;
     }
 
-    while(running_) {
+    while (running_) {
         gettimeofday(&start_tv, NULL);
         cycle_start = vm->cycle;
 
@@ -416,7 +477,6 @@ COMMAND_IMPL(run) {
             continue;
 
         /* each cycle wants 10 microseconds */
-        
 
         /* how much of that did we spend already */
         gettimeofday(&now_tv, NULL);
@@ -424,19 +484,26 @@ COMMAND_IMPL(run) {
         /* do we have time to kill? */
         if (cycles_to_wait * MICROSECONDS_PER_CYCLE > diff_tv.tv_usec) {
             sleep_time.tv_sec = diff_tv.tv_sec;
-            sleep_time.tv_nsec = 1000 * ( (cycles_to_wait * MICROSECONDS_PER_CYCLE) - diff_tv.tv_usec);
-            sleep_time.tv_nsec = diff_tv.tv_usec * 1000;
+            /* this is not accurate.. */
+            sleep_time.tv_nsec = 250 * ( (cycles_to_wait * MICROSECONDS_PER_CYCLE) - diff_tv.tv_usec);
 
-            nanosleep(&sleep_time, NULL);
+            /* nanosleep doesn't interfere with libvncserver, unlike usleep */
+            while ( nanosleep(&sleep_time, &rem_time) ) {
+                sleep_time = rem_time;
+                fprintf(stderr, "rem:%ld %ld\n", rem_time.tv_sec, rem_time.tv_nsec);
+            }
         }
     }
 
     gettimeofday(&run_stop_tv, NULL);
     timeval_subtract(&diff_tv, &run_stop_tv, &run_start_tv);
-    fprintf(stderr, "ran %llu cycles in %lds %dus\n",
+    run_usec = diff_tv.tv_sec * 1000000;
+    run_usec += diff_tv.tv_usec;
+    fprintf(stderr, "ran %llu cycles in %lds %dus (%lldus)\n",
             vm->cycle - run_cycle_start,
             diff_tv.tv_sec,
-            diff_tv.tv_usec);
+            diff_tv.tv_usec,
+            run_usec);
 
     printf("interrupted...\n");
 
@@ -458,14 +525,15 @@ static const char * const display_filename_default_ =
 #endif /* HAVE_LIBPNG */
 ;
 COMMAND_IMPL(display) {
-    struct dcpu16_hw *hw = lem1802_new(vm);
+    struct dcpu16_hw *hw;
     const char *renderer = arg_vector[1];
     const char *renderer_arg = NULL;
-    void *renderer_data = NULL;
+    void *renderer_data;
 
     if (arg_count == 3)
         renderer_arg = arg_vector[2];
 
+    hw = lem1802_new(vm);
     if (hw == NULL) {
         fprintf(stderr, "failed to initialize new display\n");
         return 0;
@@ -491,17 +559,19 @@ COMMAND_IMPL(display) {
     if (strcmp(renderer, "vnc") == 0) {
         int argc = 1;
         char *argv[] = { "vm-dcpu16", NULL };
+        struct rfb_instance_ *s;
 
-        renderer_data = lem1802_vnc_init_data(argc, argv, hw);
-
-        /* FIXME: keep refs to vnc displays around somewhere, in global list maybe.. */
-        /* keyboards will want to attach to them as well.. */
-
-        if (renderer_data == NULL) {
+        s = rfbScreen_next_available_display_(&rfbScreens_, argc, argv);
+        if (s == NULL) {
             fprintf(stderr, "failed to initialize vnc\n");
             lem1802_del(&hw);
             return 0;
         }
+
+        lem1802_vnc_associate(hw, s->screen);
+        s->attached_display = hw;
+        rfbScreen_start(s->screen);
+        renderer_data = s->screen;
     }
 #endif /* HAVE_LIBVNCSERVER */
 
@@ -536,6 +606,47 @@ COMMAND_HELP(display) {
     }
 }
 
+COMMAND_IMPL(keyboard) {
+    struct dcpu16_hw *hw;
+
+    (void)arg_count, (void)arg_vector;
+
+    hw = keyboard_new(vm);
+    if (hw == NULL) {
+        fprintf(stderr, "failed to initialize new keyboard\n");
+        return 0;
+    }
+
+#ifdef HAVE_LIBVNCSERVER
+    struct rfb_instance_ *s;
+    int argc = 1;
+    char *argv[] = { "vm-dcpu16", NULL };
+
+    s = rfbScreen_next_available_keyboard_(&rfbScreens_, argc, argv);
+    if (s == NULL) {
+        fprintf(stderr, "failed to initialize vnc\n");
+        keyboard_del(&hw);
+        return 0;
+    }
+    keyboard_vnc_associate(hw, s->screen);
+    s->attached_keyboard = hw;
+
+    if (dcpu16_hw_add(vm, hw)) {
+        fprintf(stderr, "failed to attach new keyboard\n");
+        keyboard_del(&hw);
+        return 0;
+    }
+#endif /* HAVE_LIBVNCSERVER */
+
+    return 0;
+}
+COMMAND_HELP(keyboard) {
+    fprintf(f, "\tkeyboard\n");
+    if (summary) return;
+
+    fprintf(f, "Attaches new keyboard unit.\n");
+}
+
 /* gather all these together into a searchable table */
 
 /* help command gets some assistance in declarations */
@@ -553,6 +664,7 @@ static struct command_ command_table_[] = {
     COMMAND_ENTRY(set, 2, 2),
     COMMAND_ENTRY(reset, 0, 0),
     COMMAND_ENTRY(display, 1, 2),
+    COMMAND_ENTRY(keyboard, 0, 0),
     { NULL, 0, 0, NULL, NULL }
 };
 
@@ -623,6 +735,13 @@ int main(int argc, char **argv) {
         exit(EX_UNAVAILABLE);
     }
 
+#ifdef HAVE_LIBVNCSERVER
+    if (dynarray_init(&rfbScreens_, sizeof(struct rfb_instance_), 4)) {
+        fprintf(stderr, "could not allocate rfb container\n");
+        exit(EX_UNAVAILABLE);
+    }
+#endif /* HAVE_LIBVNCSERVER */
+
     if (argc) {
         if (file_load_(vm, *argv, 0)) {
             fprintf(stderr, "couldn't load '%s'\n", *argv);