keyboard now processes keys via rfb. framework in place for multiple modules.
authorJustin Wind <justin.wind@gmail.com>
Tue, 15 May 2012 07:08:55 +0000 (00:08 -0700)
committerJustin Wind <justin.wind@gmail.com>
Tue, 15 May 2012 07:08:55 +0000 (00:08 -0700)
Continuing refinement to hardware-module interfaces and driver internals while wiring up vnc library.

hw_keyboard.c
hw_keyboard.h
hw_lem1802.c
hw_lem1802.h
vm-dcpu16.c

index 71e016f652e2e6acf703077e3357b3b6009c6380..28d66d63c6c0f16a14cdfe57403a104ecd170ca4 100644 (file)
@@ -2,6 +2,11 @@
 #include <string.h>
 #include <errno.h>
 
+#ifdef HAVE_LIBVNCSERVER
+#include <rfb/rfb.h>
+#include <rfb/keysym.h>
+#endif /* HAVE_LIBVNCSERVER */
+
 #include "dcpu16.h"
 #include "hw_keyboard.h"
 
@@ -11,6 +16,7 @@ static dcpu16_hw_signal_t keyboard_reset_;
 static dcpu16_hw_signal_t keyboard_cycle_;
 static dcpu16_hw_signal_t keyboard_hwi_;
 static struct dcpu16_hw hw_ = {
+    .vm     = NULL,
     .name_  = "Generic Keyboard (compatible)",
     .id_l   = 0x7406,
     .id_h   = 0x30cf,
@@ -27,8 +33,73 @@ struct keyboard_ {
     char *buf;
     size_t buf_sz;
     DCPU16_WORD interrupt_message;
+    unsigned char keys_pressed[256];
+};
+
+#ifdef HAVE_LIBVNCSERVER
+
+static const struct keysym_map_ {
+    unsigned int rfb_start;
+    unsigned int rfb_end;
+    DCPU16_WORD dcpu_start;
+} keymap_[] = {
+    { XK_space,     XK_asciitilde,  0x20 }, /* ASCII range */
+    { XK_Delete,    XK_Delete,      0x7f }, /* ASCII del */
+    { XK_BackSpace, XK_BackSpace,   0x10 }, /* bs */
+    { XK_Return,    XK_Return,      0x11 }, /* ret */
+    { XK_Insert,    XK_Insert,      0x12 }, /* ins */
+    { XK_Delete,    XK_Delete,      0x13 }, /* del */
+    { XK_Up,        XK_Up,          0x80 }, /* arrow up */
+    { XK_Down,      XK_Down,        0x81 }, /* arrow down */
+    { XK_Left,      XK_Left,        0x82 }, /* arrow left */
+    { XK_Right,     XK_Right,       0x83 }, /* arrow right */
+    { XK_Shift_L,   XK_Shift_R,     0x90 }, /* shift range */
+    { XK_Control_L, XK_Control_R,   0x91 }, /* control range */
+    { 0, 0, 0x0 }
 };
 
+static int keysym_rfbtodcpu(unsigned int rfb, DCPU16_WORD *dcpu) {
+    const struct keysym_map_ *map;
+
+    for (map = keymap_; map->rfb_start; map++) {
+        if (rfb >= map->rfb_start
+        &&  rfb <= map->rfb_end) {
+            *dcpu = (map->dcpu_start + (rfb - map->rfb_start));
+            return 0;
+        }
+    }
+
+    *dcpu = 0x00;
+    return -1;
+}
+
+static
+void keyboard_rfbevent_(rfbBool down, rfbKeySym key, rfbClientPtr cl) {
+    DCPU16_WORD dcpu_key;
+    struct dcpu16_hw *hw = (struct dcpu16_hw *)cl->screen->screenData;
+    struct keyboard_ *keyboard = (struct keyboard_ *)hw->data;
+
+    keysym_rfbtodcpu(key, &dcpu_key);
+
+    fprintf(stderr, "%s: down:%u key:0x%04x dcpu_key:0x%04x\n", __func__,
+            down, key, dcpu_key);
+
+    fprintf(stderr, "%s: hw:%p name:%s vm:%p\n", __func__, hw, hw->name_, hw->vm);
+
+    keyboard->keys_pressed[dcpu_key] = (down ? 1 : 0);
+    if (down)
+        keyboard->buf[0] = dcpu_key;
+    if (keyboard->interrupt_message) {
+        dcpu16_interrupt(hw->vm, keyboard->interrupt_message);
+    }
+}
+
+void keyboard_vnc_associate(struct dcpu16_hw *hw, rfbScreenInfoPtr rfbScreen) {
+    rfbScreen->screenData = hw;
+    rfbScreen->kbdAddEvent = keyboard_rfbevent_;
+}
+#endif /* HAVE_LIBVNCSERVER */
+
 static
 void keyboard_reset_(struct dcpu16 *vm, void *data) {
     struct keyboard_ *keyboard = (struct keyboard_ *)data;
@@ -67,9 +138,7 @@ void keyboard_hwi_(struct dcpu16 *vm, void *data) {
             break;
 
         case 2: /* get currently-pressed-state of key in B as C */
-            vm->warn_cb_("IMPLEMENT");
-            (void)reg_b;
-            vm->reg[DCPU16_REG_C] = 0;
+            vm->reg[DCPU16_REG_C] = keyboard->keys_pressed[reg_b & 0x00ff];
             break;
 
         case 3: /* set interrupt state */
@@ -106,6 +175,8 @@ struct dcpu16_hw *keyboard_new(struct dcpu16 *vm) {
     ((struct keyboard_ *)(hw->data))->buf = b;
     ((struct keyboard_ *)(hw->data))->buf_sz = BUF_SZ;
 
+    hw->vm = vm;
+
     return hw;
 }
 
index cf7eb4c9c9c0b531fb7dc282350ade848614beeb..a36047651b82f856394e338085fcc0993a91426b 100644 (file)
@@ -1,7 +1,17 @@
 #ifndef KEYBOARD_H_Y2G5EOAS
 #define KEYBOARD_H_Y2G5EOAS
 
+#ifdef HAVE_LIBVNCSERVER
+#include "rfb/rfb.h"
+#endif /* HAVE_LIBVNCSERVER */
+
+#include "dcpu16.h"
+
 struct dcpu16_hw *keyboard_new(struct dcpu16 *);
 void keyboard_del(struct dcpu16_hw **);
 
+#ifdef HAVE_LIBVNCSERVER
+void keyboard_vnc_associate(struct dcpu16_hw *, rfbScreenInfoPtr);
+#endif /* HAVE_LIBVNCSERVER */
+
 #endif /* KEYBOARD_H_Y2G5EOAS */
index dd8d33fdcb48fcdda46cef9f0dd718fc09042f30..1be1810fa933720c3da6436b04be742d1633248e 100644 (file)
@@ -17,7 +17,6 @@
 #include "chargen-4x8.h"
 #include "hw_lem1802.h"
 
-#undef DEBUG
 #ifdef DEBUG
 #define TRACE(...) do { printf("[debug] "); printf(__VA_ARGS__); printf("\n"); } while (0)
 #else /* DEBUG  */
@@ -142,10 +141,12 @@ void font_tile_paint_(struct pixel_ *p, struct pixel_ *fg, struct pixel_ *bg, DC
     size_t pix_x, pix_y;
     unsigned char *font_bitmap = (unsigned char *)tile;
 
+#if 0
     TRACE("%s>> fg:(%u,%u,%u) bg:(%u,%u,%u) font_bitmap:%02x %02x %02x %02x", __func__,
           fg->r, fg->g, fg->b,
           bg->r, bg->g, bg->b,
           font_bitmap[0], font_bitmap[1], font_bitmap[2], font_bitmap[3]);
+#endif
 
     for (pix_x = 0; pix_x < CELL_X_SZ; pix_x++) {
         for (pix_y = 0; pix_y < CELL_Y_SZ; pix_y++) {
@@ -169,10 +170,12 @@ void pixbuf_addr_paint_(struct pixel_ *pixbuf, DCPU16_WORD *mem, DCPU16_WORD bas
     cell_x = (addr - base) % CELL_X;
     cell_y = (addr - base) / CELL_X;
 
+#if 0
     TRACE("%s>> addr:0x%04x col:%u row:%u v:%hu",
           __func__,
           addr,
           cell_x, cell_y, mem[addr]);
+#endif
 
     blink = mem[addr] & 0x0080;
 
@@ -199,7 +202,9 @@ void lem1802_pixbuf_refresh_full_(struct lem1802_ *display, DCPU16_WORD *mem) {
     struct pixel_ border;
     size_t tile;
 
+#if 0
     TRACE("%s>> video_base:0x%04x", __func__, display->video_base);
+#endif
 
     if (display->video_base == 0) {
         /* disconnected, blank display.  static might be fun, too */
@@ -241,14 +246,6 @@ int pixbuf_render_pnm_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
 
 #ifdef HAVE_LIBPNG
 static
-void pixbuf_render_png_user_warning_(png_structp png, png_const_charp msg) {
-    (void)png, (void)msg;
-}
-static
-void pixbuf_render_png_user_error_(png_structp png, png_const_charp msg) {
-    (void)png, (void)msg;
-}
-static
 int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
     FILE *f = (FILE *)data;
     int retval = 0;
@@ -256,7 +253,7 @@ int pixbuf_render_png_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
     png_infop info;
     size_t i;
 
-    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, pixbuf_render_png_user_error_, pixbuf_render_png_user_warning_);
+    png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
     if (png == NULL) {
         goto f_done;
     }
@@ -291,58 +288,51 @@ f_done:
 #endif /* HAVE_LIBPNG */
 
 #ifdef HAVE_LIBVNCSERVER
-struct vnc_server_ {
-    rfbScreenInfoPtr rfbScreen;
-};
-
-/* create and return a new struct vnc_server_, for use as a vnc renderer's generic data */
-void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw) {
-    struct vnc_server_ *s;
-    struct pixel_ *pixbuf = ((struct lem1802_ *)(hw->data))->pixbuf;
+/* create and return a new screen, easiest to do here because we know the screen dimensions */
+rfbScreenInfoPtr lem1802_rfb_new(int argc, char *argv[]) {
+    rfbScreenInfoPtr s;
     int paddedWidth = PIX_X + ( (PIX_X & 3) ? (4 - (PIX_X & 3)) : 0 );
     int height = PIX_Y;
     int bitsPerSample = 8;
     int samplesPerPixel = 3;
     int bytesPerPixel = 4;
 
-    s = calloc(1, sizeof *s);
+    s = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel);
     if (s == NULL)
         return NULL;
 
-    s->rfbScreen = rfbGetScreen(&argc, argv, paddedWidth, height, bitsPerSample, samplesPerPixel, bytesPerPixel);
-    if (s->rfbScreen == NULL) {
-        free(s);
-        return NULL;
-    }
-
-    s->rfbScreen->desktopName = "NYA ELEKTRISKA LEM1802";
-    s->rfbScreen->alwaysShared = TRUE;
+    s->alwaysShared = TRUE;
 
 #if 0
-    s->rfbScreen->kbdAddEvent = HandleKey; */
-    s->rfbScreen->httpDir = "../classes";
+    s->httpDir = "../classes";
 #endif
 
-    s->rfbScreen->frameBuffer = (char *)pixbuf;
+    TRACE("%s>> s:%p", __func__, s);
+    TRACE("%s>> s->kbdAddEvent:%p s->frameBuffer:%p", __func__, s->kbdAddEvent, s->frameBuffer);
 
-    rfbInitServer(s->rfbScreen);
+    return s;
+}
 
-    rfbRunEventLoop(s->rfbScreen,-1,TRUE);
+/* set up a new screen to see our pixels */
+void lem1802_vnc_associate(struct dcpu16_hw *hw, rfbScreenInfoPtr s) {
+    struct lem1802_ *display = (struct lem1802_ *)hw->data;
 
-    TRACE("%s>> initialized new vnc server", __func__);
+    s->desktopName = "NYA ELEKTRISKA LEM1802";
+    s->frameBuffer = (char *)display->pixbuf;
 
-    return s;
+    TRACE("%s>> s:%p\n", __func__, s);
 }
 
+/* notify rfb server that pixels may have changed */
 static
 int pixbuf_render_vnc_(void *data, struct pixel_ *pixbuf, size_t x, size_t y) {
+    rfbScreenInfoPtr s = (rfbScreenInfoPtr)data;
     int retval = 0;
-    struct vnc_server_ *s = (struct vnc_server_ *)data;
 
     (void)pixbuf;
 
     /* derp */
-    rfbMarkRectAsModified(s->rfbScreen,0,0,x,y);
+    rfbMarkRectAsModified(s, 0, 0, x, y);
 
     TRACE("%s>>", __func__);
 
@@ -536,7 +526,9 @@ struct dcpu16_hw *lem1802_new(struct dcpu16 *vm) {
     }
 
     ((struct lem1802_ *)(hw->data))->refresh_rate = 1666;
-    ((struct lem1802_ *)(hw->data))->blink_rate = 100000;
+    ((struct lem1802_ *)(hw->data))->blink_rate = 75000;
+
+    hw->vm = vm;
 
     return hw;
 }
index fb739f42f4b7310a145ecfddc71f77707f0bf4d0..cc45d682bcafdc5d1e1b316ac377a8ea20e0aa6d 100644 (file)
@@ -1,12 +1,19 @@
 #ifndef LEM1802_H_WH5E5NOE
 #define LEM1802_H_WH5E5NOE
 
+#ifdef HAVE_LIBVNCSERVER
+#include "rfb/rfb.h"
+#endif /* HAVE_LIBVNCSERVER */
+
 #include "dcpu16.h"
 
 struct dcpu16_hw *lem1802_new(struct dcpu16 *);
 void lem1802_del(struct dcpu16_hw **);
 int lem1802_renderer_set(struct dcpu16_hw *, const char *, void *);
 char *lem1802_renderers_iter(void **, char **, char **);
-void *lem1802_vnc_init_data(int argc, char *argv[], struct dcpu16_hw *hw);
+#ifdef HAVE_LIBVNCSERVER
+rfbScreenInfoPtr lem1802_rfb_new(int argc, char *argv[]);
+void lem1802_vnc_associate(struct dcpu16_hw *, rfbScreenInfoPtr);
+#endif /* HAVE_LIBVNCSERVER */
 
 #endif /* LEM1802_H_WH5E5NOE */
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);