Continuing refinement to hardware-module interfaces and driver internals while wiring up vnc library.
#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"
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,
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;
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 */
((struct keyboard_ *)(hw->data))->buf = b;
((struct keyboard_ *)(hw->data))->buf_sz = BUF_SZ;
+ hw->vm = vm;
+
return hw;
}
#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 */
#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 */
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++) {
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;
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 */
#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;
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;
}
#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__);
}
((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;
}
#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 */
#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
}
+#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.
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;
return -1;
}
- while(running_) {
+ while (running_) {
gettimeofday(&start_tv, NULL);
cycle_start = vm->cycle;
continue;
/* each cycle wants 10 microseconds */
-
/* how much of that did we spend already */
gettimeofday(&now_tv, NULL);
/* 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");
#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;
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 */
}
}
+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 */
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 }
};
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);