qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] VNC display support for QEMU


From: Anthony Liguori
Subject: [Qemu-devel] [PATCH] VNC display support for QEMU
Date: Sat, 29 Apr 2006 16:34:22 -0500
User-agent: Mail/News 1.5 (X11/20060309)

Hi,

The attach patch adds VNC display support for QEMU. It does not use libvncserver but was rather written from scratch. libvncserver is a really neat project and I've used it in a number of other projects but I think QEMU really requires a custom implementation.

First, to enable vnc support, you use the -vnc option like:

qemu -vnc 1

Where 1 is the first display (port 5901). This syntax may change in the near future to support binding to a particular interface. It's very useful to use an absolute mouse with VNC as the relative support is quite poor. It may be useful to adapt the libvncserver patch's calibration code here but I've not attempted to do that yet.

This patch is still experimental. I've tested it with RealVNC and TightVNC under a variety of depths but I won't be suprised if there are still problems. I only implement Raw, CopyRect, and Hextile encodings too. Any sort of palette color mode or pixel format that QEMU doesn't support will not work either.

One thing you may notice is that RealVNC has some issues with being disconnected. This is because it likes to switch from 8bit to 32bit depths automatically at startup. Unfortunately, there is a race condition in the VNC protocol and since this implementation is asynchronous, we seem to be much more prone to exposing this.

A short near-term TODO list is:

1) More testing
2) Support switching between monitor/serial
3) Support a better encoding (like TightEncoding or ZRLE)
4) Support a vnc password (and perhaps stuff like TLS)

Any feedback is greatly appreciated (especially with how it works with clients I've not tested).

Regards,

Anthony Liguori
diff -urN -x '*.[di]' a.hg/hw/cirrus_vga.c vnc.hg/hw/cirrus_vga.c
--- a.hg/hw/cirrus_vga.c        2006-04-29 16:01:36.000000000 -0500
+++ vnc.hg/hw/cirrus_vga.c      2006-04-29 16:18:46.000000000 -0500
@@ -644,15 +644,90 @@
                                             (s->cirrus_blt_srcaddr & ~7));
 }
 
-static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
 {
+    int sx, sy;
+    int dx, dy;
+    int width, height;
+    int depth;
+    int notify = 0;
+
+    depth = s->get_bpp((VGAState *)s) / 8;
+    s->get_resolution((VGAState *)s, &width, &height);
+
+    /* extra x, y */
+    sx = (src % (width * depth)) / depth;
+    sy = (src / (width * depth));
+    dx = (dst % (width *depth)) / depth;
+    dy = (dst / (width * depth));
+
+    /* normalize width */
+    w /= depth;
+
+    /* if we're doing a backward copy, we have to adjust
+       our x/y to be the upper left corner (instead of the lower
+       right corner) */
+    if (s->cirrus_blt_dstpitch < 0) {
+       sx -= (s->cirrus_blt_width / depth) - 1;
+       dx -= (s->cirrus_blt_width / depth) - 1;
+       sy -= s->cirrus_blt_height - 1;
+       dy -= s->cirrus_blt_height - 1;
+    }
+
+    /* are we in the visible portion of memory? */
+    if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+       (sx + w) <= width && (sy + h) <= height &&
+       (dx + w) <= width && (dy + h) <= height) {
+       notify = 1;
+    }
+
+    /* make to sure only copy if it's a plain copy ROP */
+    if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src &&
+       *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src)
+       notify = 0;
+
+    /* we have to flush all pending changes so that the copy
+       is generated at the appropriate moment in time */
+    if (notify)
+       vga_hw_update();
+
     (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
                      s->vram_ptr + s->cirrus_blt_srcaddr,
                      s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
                      s->cirrus_blt_width, s->cirrus_blt_height);
-    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
-                            s->cirrus_blt_dstpitch, s->cirrus_blt_width,
-                            s->cirrus_blt_height);
+
+    if (notify)
+       s->ds->dpy_copy(s->ds,
+                       sx, sy, dx, dy,
+                       s->cirrus_blt_width / depth,
+                       s->cirrus_blt_height);
+
+    /* we don't have to notify the display that this portion has
+       changed since dpy_copy implies this */
+
+    if (!notify)
+       cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                                s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                                s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+    if (s->ds->dpy_copy) {
+       cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr,
+                      s->cirrus_blt_srcaddr - s->start_addr,
+                      s->cirrus_blt_width, s->cirrus_blt_height);
+    } else {
+       (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
+                         s->vram_ptr + s->cirrus_blt_srcaddr,
+                         s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+                         s->cirrus_blt_width, s->cirrus_blt_height);
+
+       cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                                s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                                s->cirrus_blt_height);
+    }
+
     return 1;
 }
 
diff -urN -x '*.[di]' a.hg/Makefile.target vnc.hg/Makefile.target
--- a.hg/Makefile.target        2006-04-29 16:01:34.000000000 -0500
+++ vnc.hg/Makefile.target      2006-04-29 16:20:17.000000000 -0500
@@ -357,6 +357,7 @@
 ifdef CONFIG_SDL
 VL_OBJS+=sdl.o
 endif
+VL_OBJS+=vnc.o
 ifdef CONFIG_COCOA
 VL_OBJS+=cocoa.o
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -409,6 +410,9 @@
 sdl.o: sdl.c keymaps.c sdl_keysym.h
        $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
 
+vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h
+       $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
+
 sdlaudio.o: sdlaudio.c
        $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $<
 
diff -urN -x '*.[di]' a.hg/qemu-doc.texi vnc.hg/qemu-doc.texi
--- a.hg/qemu-doc.texi  2006-04-29 16:01:38.000000000 -0500
+++ vnc.hg/qemu-doc.texi        2006-04-29 15:55:58.000000000 -0500
@@ -185,6 +185,13 @@
 the console. Therefore, you can still use QEMU to debug a Linux kernel
 with a serial console.
 
address@hidden -vnc d
+
+Normally, QEMU uses SDL to display the VGA output.  With this option,
+you can have QEMU listen on VNC display d and redirect the VGA display
+over the VNC session.  It is very useful to enable the usb tablet device
+when using this option.
+
 @item -k language
 
 Use keyboard layout @var{language} (for example @code{fr} for
diff -urN -x '*.[di]' a.hg/vl.c vnc.hg/vl.c
--- a.hg/vl.c   2006-04-29 16:01:43.000000000 -0500
+++ vnc.hg/vl.c 2006-04-27 23:44:41.000000000 -0500
@@ -149,6 +149,7 @@
 USBDevice *vm_usb_hub;
 static VLANState *first_vlan;
 int smp_cpus = 1;
+int vnc_display = -1;
 #if defined(TARGET_SPARC)
 #define MAX_CPUS 16
 #elif defined(TARGET_I386)
@@ -4638,6 +4639,7 @@
            "                (default is CL-GD5446 PCI VGA)\n"
 #endif
            "-loadvm file    start right away with a saved state (loadvm in 
monitor)\n"
+          "-vnc display    start a VNC server on display\n"
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -4721,6 +4723,7 @@
     QEMU_OPTION_usb,
     QEMU_OPTION_usbdevice,
     QEMU_OPTION_smp,
+    QEMU_OPTION_vnc,
 };
 
 typedef struct QEMUOption {
@@ -4788,6 +4791,7 @@
     { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
     { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
     { "smp", HAS_ARG, QEMU_OPTION_smp },
+    { "vnc", HAS_ARG, QEMU_OPTION_vnc },
     
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
@@ -5386,6 +5390,13 @@
                     exit(1);
                 }
                 break;
+           case QEMU_OPTION_vnc:
+               vnc_display = atoi(optarg);
+               if (vnc_display < 0) {
+                   fprintf(stderr, "Invalid VNC display\n");
+                   exit(1);
+               }
+               break;
             }
         }
     }
@@ -5551,6 +5562,8 @@
     /* terminal init */
     if (nographic) {
         dumb_display_init(ds);
+    } if (vnc_display != -1) {
+       vnc_display_init(ds, vnc_display);
     } else {
 #if defined(CONFIG_SDL)
         sdl_display_init(ds, full_screen);
diff -urN -x '*.[di]' a.hg/vl.h vnc.hg/vl.h
--- a.hg/vl.h   2006-04-29 16:01:43.000000000 -0500
+++ vnc.hg/vl.h 2006-04-29 15:17:16.000000000 -0500
@@ -82,6 +82,13 @@
 #define tostring(s)    #s
 #endif
 
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
 /* vl.c */
 uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
 
@@ -672,9 +679,12 @@
     int depth;
     int width;
     int height;
+    void *opaque;
+
     void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h);
     void (*dpy_resize)(struct DisplayState *s, int w, int h);
     void (*dpy_refresh)(struct DisplayState *s);
+    void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y, int dst_x, 
int dst_y, int w, int h);
 };
 
 static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
@@ -703,6 +713,9 @@
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
+/* vnc.c */
+void vnc_display_init(DisplayState *ds, int display);
+
 /* ide.c */
 #define MAX_DISKS 4
 
diff -urN -x '*.[di]' a.hg/vnc.c vnc.hg/vnc.c
--- a.hg/vnc.c  1969-12-31 18:00:00.000000000 -0600
+++ vnc.hg/vnc.c        2006-04-29 16:15:02.000000000 -0500
@@ -0,0 +1,894 @@
+#include "vl.h"
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <fcntl.h>
+
+#define VNC_REFRESH_INTERVAL (1000 / 30)
+
+#include "vnc_keysym.h"
+#include "keymaps.c"
+
+typedef struct Buffer
+{
+    size_t capacity;
+    size_t offset;
+    char *buffer;
+} Buffer;
+
+typedef struct VncState VncState;
+
+typedef int VncReadEvent(VncState *vs, char *data, size_t len);
+
+struct VncState
+{
+    QEMUTimer *timer;
+    int lsock;
+    int csock;
+    DisplayState *ds;
+    int need_update;
+    int width;
+    int height;
+    uint64_t dirty_row[768];
+    char *old_data;
+    int depth;
+    int has_resize;
+    int has_hextile;
+    Buffer output;
+    Buffer input;
+    kbd_layout_t *kbd_layout;
+
+    VncReadEvent *read_handler;
+    size_t read_handler_expect;
+};
+
+/* TODO
+   1) Get the queue working for IO.
+   2) there is some weirdness when using the -S option (the screen is grey
+      and not totally invalidated
+   3) resolutions > 1024
+*/
+
+static void vnc_write(VncState *vs, const void *data, size_t len);
+static void vnc_write_u32(VncState *vs, uint32_t value);
+static void vnc_write_s32(VncState *vs, int32_t value);
+static void vnc_write_u16(VncState *vs, uint16_t value);
+static void vnc_write_u8(VncState *vs, uint8_t value);
+static void vnc_flush(VncState *vs);
+static void vnc_update_client(void *opaque);
+static void vnc_client_read(void *opaque);
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    VncState *vs = ds->opaque;
+    int i;
+
+    h += y;
+
+    for (; y < h; y++)
+       for (i = 0; i < w; i += 16)
+           vs->dirty_row[y] |= (1ULL << ((x + i) / 16));
+}
+
+static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+                                  int32_t encoding)
+{
+    vnc_write_u16(vs, x);
+    vnc_write_u16(vs, y);
+    vnc_write_u16(vs, w);
+    vnc_write_u16(vs, h);
+
+    vnc_write_s32(vs, encoding);
+}
+
+static void vnc_dpy_resize(DisplayState *ds, int w, int h)
+{
+    VncState *vs = ds->opaque;
+
+    ds->data = realloc(ds->data, w * h * vs->depth);
+    vs->old_data = realloc(vs->old_data, w * h * vs->depth);
+
+    if (ds->data == NULL || vs->old_data == NULL) {
+       fprintf(stderr, "vnc: memory allocation failed\n");
+       exit(1);
+    }
+
+    ds->depth = vs->depth * 8;
+    ds->width = w;
+    ds->height = h;
+    ds->linesize = w * vs->depth;
+    if (vs->csock != -1 && vs->has_resize) {
+       vnc_write_u8(vs, 0);  /* msg id */
+       vnc_write_u8(vs, 0);
+       vnc_write_u16(vs, 1); /* number of rects */
+       vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
+       vnc_flush(vs);
+       vs->width = ds->width;
+       vs->height = ds->height;
+    }
+}
+
+static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int 
h)
+{
+    int i;
+    char *row;
+
+    vnc_framebuffer_update(vs, x, y, w, h, 0);
+
+    row = vs->ds->data + y * vs->ds->linesize + x * vs->depth;
+    for (i = 0; i < h; i++) {
+       vnc_write(vs, row, w * vs->depth);
+       row += vs->ds->linesize;
+    }
+}
+
+static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
+{
+    ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F);
+    ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F);
+}
+
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+
+static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, 
int h)
+{
+    int i, j;
+    int has_fg, has_bg;
+    uint32_t last_fg32, last_bg32;
+    uint16_t last_fg16, last_bg16;
+    uint8_t last_fg8, last_bg8;
+
+    vnc_framebuffer_update(vs, x, y, w, h, 5);
+
+    has_fg = has_bg = 0;
+    for (j = y; j < (y + h); j += 16) {
+       for (i = x; i < (x + w); i += 16) {
+           switch (vs->depth) {
+           case 1:
+               send_hextile_tile_8(vs, i, j, MIN(16, x + w - i), MIN(16, y + h 
- j),
+                                   &last_bg8, &last_fg8, &has_bg, &has_fg);
+               break;
+           case 2:
+               send_hextile_tile_16(vs, i, j, MIN(16, x + w - i), MIN(16, y + 
h - j),
+                                    &last_bg16, &last_fg16, &has_bg, &has_fg);
+               break;
+           case 4:
+               send_hextile_tile_32(vs, i, j, MIN(16, x + w - i), MIN(16, y + 
h - j),
+                                    &last_bg32, &last_fg32, &has_bg, &has_fg);
+               break;
+           default:
+               break;
+           }
+       }
+    }
+}
+
+static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+       if (vs->has_hextile)
+           send_framebuffer_update_hextile(vs, x, y, w, h);
+       else
+           send_framebuffer_update_raw(vs, x, y, w, h);
+}
+
+static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int 
dst_y, int w, int h)
+{
+    int src, dst;
+    char *src_row;
+    char *dst_row;
+    char *old_row;
+    int y = 0;
+    int pitch = ds->linesize;
+    VncState *vs = ds->opaque;
+
+    vnc_update_client(vs);
+
+    if (dst_y > src_y) {
+       y = h - 1;
+       pitch = -pitch;
+    }
+
+    src = (ds->linesize * (src_y + y) + vs->depth * src_x);
+    dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x);
+
+    src_row = ds->data + src;
+    dst_row = ds->data + dst;
+    old_row = vs->old_data + dst;
+
+    for (y = 0; y < h; y++) {
+       memmove(old_row, src_row, w * vs->depth);
+       memmove(dst_row, src_row, w * vs->depth);
+       src_row += pitch;
+       dst_row += pitch;
+       old_row += pitch;
+    }
+
+    vnc_write_u8(vs, 0);  /* msg id */
+    vnc_write_u8(vs, 0);
+    vnc_write_u16(vs, 1); /* number of rects */
+    vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
+    vnc_write_u16(vs, src_x);
+    vnc_write_u16(vs, src_y);
+    vnc_flush(vs);
+}
+
+static int find_dirty_height(VncState *vs, int y, int last_x, int x)
+{
+    int h;
+
+    for (h = 1; h < (vs->height - y); h++) {
+       int tmp_x;
+       if (!(vs->dirty_row[y + h] & (1ULL << last_x)))
+           break;
+       for (tmp_x = last_x; tmp_x < x; tmp_x++)
+           vs->dirty_row[y + h] &= ~(1ULL << tmp_x);
+    }
+
+    return h;
+}
+
+static void vnc_update_client(void *opaque)
+{
+    VncState *vs = opaque;
+
+    if (vs->need_update && vs->csock != -1) {
+       int y;
+       char *row;
+       char *old_row;
+       uint64_t width_mask;
+       int n_rectangles;
+       int saved_offset;
+       int has_dirty = 0;
+
+       width_mask = (1ULL << (vs->width / 16)) - 1;
+
+       if (vs->width == 1024)
+           width_mask = ~(0ULL);
+
+       /* Walk through the dirty map and eliminate tiles that
+          really aren't dirty */
+       row = vs->ds->data;
+       old_row = vs->old_data;
+
+       for (y = 0; y < vs->height; y++) {
+           if (vs->dirty_row[y] & width_mask) {
+               int x;
+               char *ptr, *old_ptr;
+
+               ptr = row;
+               old_ptr = old_row;
+
+               for (x = 0; x < vs->ds->width; x += 16) {
+                   if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) {
+                       vs->dirty_row[y] &= ~(1ULL << (x / 16));
+                   } else {
+                       has_dirty = 1;
+                       memcpy(old_ptr, ptr, 16 * vs->depth);
+                   }
+
+                   ptr += 16 * vs->depth;
+                   old_ptr += 16 * vs->depth;
+               }
+           }
+
+           row += vs->ds->linesize;
+           old_row += vs->ds->linesize;
+       }
+
+       if (!has_dirty) {
+           qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + 
VNC_REFRESH_INTERVAL);
+           return;
+       }
+
+       /* Count rectangles */
+       n_rectangles = 0;
+       vnc_write_u8(vs, 0);  /* msg id */
+       vnc_write_u8(vs, 0);
+       saved_offset = vs->output.offset;
+       vnc_write_u16(vs, 0);
+
+       for (y = 0; y < vs->height; y++) {
+           int x;
+           int last_x = -1;
+           for (x = 0; x < vs->width / 16; x++) {
+               if (vs->dirty_row[y] & (1ULL << x)) {
+                   if (last_x == -1) {
+                       last_x = x;
+                   }
+                   vs->dirty_row[y] &= ~(1ULL << x);
+               } else {
+                   if (last_x != -1) {
+                       int h = find_dirty_height(vs, y, last_x, x);
+                       send_framebuffer_update(vs, last_x * 16, y, (x - 
last_x) * 16, h);
+                       n_rectangles++;
+                   }
+                   last_x = -1;
+               }
+           }
+           if (last_x != -1) {
+               int h = find_dirty_height(vs, y, last_x, x);
+               send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, 
h);
+               n_rectangles++;
+           }
+       }
+       vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+       vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+       vnc_flush(vs);
+
+    }
+    qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+}
+
+static void vnc_timer_init(VncState *vs)
+{
+    if (vs->timer == NULL) {
+       vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+       qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock));
+    }
+}
+
+static void vnc_dpy_refresh(DisplayState *ds)
+{
+    VncState *vs = ds->opaque;
+    vnc_timer_init(vs);
+    vga_hw_update();
+}
+
+static int vnc_listen_poll(void *opaque)
+{
+    VncState *vs = opaque;
+    if (vs->csock == -1)
+       return 1;
+    return 0;
+}
+
+static void buffer_reserve(Buffer *buffer, size_t len)
+{
+    if ((buffer->capacity - buffer->offset) < len) {
+       buffer->capacity += (len + 1024);
+       buffer->buffer = realloc(buffer->buffer, buffer->capacity);
+       if (buffer->buffer == NULL) {
+           fprintf(stderr, "vnc: out of memory\n");
+           exit(1);
+       }
+    }
+}
+
+static int buffer_empty(Buffer *buffer)
+{
+    return buffer->offset == 0;
+}
+
+static char *buffer_end(Buffer *buffer)
+{
+    return buffer->buffer + buffer->offset;
+}
+
+static void buffer_reset(Buffer *buffer)
+{
+       buffer->offset = 0;
+}
+
+static void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+    memcpy(buffer->buffer + buffer->offset, data, len);
+    buffer->offset += len;
+}
+
+static int vnc_client_io_error(VncState *vs, int ret)
+{
+    if (ret == 0 || ret == -1) {
+       if (ret == -1 && (errno == EINTR || errno == EAGAIN))
+           return 0;
+
+       qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+       close(vs->csock);
+       vs->csock = -1;
+       buffer_reset(&vs->input);
+       buffer_reset(&vs->output);
+       vs->need_update = 0;
+       return 0;
+    }
+    return ret;
+}
+
+static void vnc_client_error(VncState *vs)
+{
+    errno = EINVAL;
+    vnc_client_io_error(vs, -1);
+}
+
+static void vnc_client_write(void *opaque)
+{
+    ssize_t ret;
+    VncState *vs = opaque;
+
+    ret = write(vs->csock, vs->output.buffer, vs->output.offset);
+    ret = vnc_client_io_error(vs, ret);
+    if (!ret)
+       return;
+
+    memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - 
ret));
+    vs->output.offset -= ret;
+
+    if (vs->output.offset == 0) {
+       qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+    }
+}
+
+static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+{
+    vs->read_handler = func;
+    vs->read_handler_expect = expecting;
+}
+
+static void vnc_client_read(void *opaque)
+{
+    VncState *vs = opaque;
+    ssize_t ret;
+
+    buffer_reserve(&vs->input, 4096);
+
+    ret = read(vs->csock, buffer_end(&vs->input), 4096);
+    ret = vnc_client_io_error(vs, ret);
+    if (!ret)
+       return;
+
+    vs->input.offset += ret;
+
+    while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+       size_t len = vs->read_handler_expect;
+       int ret;
+
+       ret = vs->read_handler(vs, vs->input.buffer, len);
+       if (vs->csock == -1)
+           return;
+
+       if (!ret) {
+           memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset 
- len));
+           vs->input.offset -= len;
+       } else {
+           vs->read_handler_expect = ret;
+       }
+    }
+}
+
+static void vnc_write(VncState *vs, const void *data, size_t len)
+{
+    buffer_reserve(&vs->output, len);
+
+    if (buffer_empty(&vs->output)) {
+       qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, 
vnc_client_write, vs);
+    }
+
+    buffer_append(&vs->output, data, len);
+}
+
+static void vnc_write_s32(VncState *vs, int32_t value)
+{
+    vnc_write_u32(vs, *(uint32_t *)&value);
+}
+
+static void vnc_write_u32(VncState *vs, uint32_t value)
+{
+    uint8_t buf[4];
+
+    buf[0] = (value >> 24) & 0xFF;
+    buf[1] = (value >> 16) & 0xFF;
+    buf[2] = (value >>  8) & 0xFF;
+    buf[3] = value & 0xFF;
+
+    vnc_write(vs, buf, 4);
+}
+
+static void vnc_write_u16(VncState *vs, uint16_t value)
+{
+    char buf[2];
+
+    buf[0] = (value >> 8) & 0xFF;
+    buf[1] = value & 0xFF;
+
+    vnc_write(vs, buf, 2);
+}
+
+static void vnc_write_u8(VncState *vs, uint8_t value)
+{
+    vnc_write(vs, (char *)&value, 1);
+}
+
+static void vnc_flush(VncState *vs)
+{
+    if (vs->output.offset)
+       vnc_client_write(vs);
+}
+
+static uint8_t read_u8(char *data, size_t offset)
+{
+    return data[offset];
+}
+
+static uint16_t read_u16(char *data, size_t offset)
+{
+    return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
+}
+
+static int32_t read_s32(char *data, size_t offset)
+{
+    return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
+                    (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static uint32_t read_u32(char *data, size_t offset)
+{
+    return ((data[offset] << 24) | (data[offset + 1] << 16) |
+           (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static void client_cut_text(VncState *vs, size_t len, char *text)
+{
+}
+
+static void pointer_event(VncState *vs, int button_mask, int x, int y)
+{
+    int buttons = 0;
+    int dz = 0;
+
+    if (button_mask & 0x01)
+       buttons |= MOUSE_EVENT_LBUTTON;
+    if (button_mask & 0x02)
+       buttons |= MOUSE_EVENT_MBUTTON;
+    if (button_mask & 0x04)
+       buttons |= MOUSE_EVENT_RBUTTON;
+    if (button_mask & 0x08)
+       dz = -1;
+    if (button_mask & 0x10)
+       dz = 1;
+           
+    if (kbd_mouse_is_absolute()) {
+       kbd_mouse_event(x * 0x7FFF / vs->ds->width,
+                       y * 0x7FFF / vs->ds->height,
+                       dz, buttons);
+    } else {
+       static int last_x = -1;
+       static int last_y = -1;
+
+       if (last_x != -1)
+           kbd_mouse_event(x - last_x, y - last_y, dz, buttons);
+
+       last_x = x;
+       last_y = y;
+    }
+}
+
+static void key_event(VncState *vs, int down, uint32_t sym)
+{
+    int keycode;
+
+    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+
+    if (keycode & 0x80)
+       kbd_put_keycode(0xe0);
+    if (down)
+       kbd_put_keycode(keycode & 0x7f);
+    else
+       kbd_put_keycode(keycode | 0x80);
+}
+
+static void framebuffer_update_request(VncState *vs, int incremental,
+                                      int x_position, int y_position,
+                                      int w, int h)
+{
+    int i;
+    vs->need_update = 1;
+    if (!incremental) {
+       char *old_row = vs->old_data + y_position * vs->ds->linesize;
+
+       for (i = 0; i < h; i++) {
+           vs->dirty_row[y_position + i] = (1ULL << (vs->ds->width / 16)) - 1;
+           if (vs->ds->width == 1024) {
+             vs->dirty_row[y_position + i] = ~(0ULL);
+           }
+           memset(old_row, 42, vs->ds->width * vs->depth);
+           old_row += vs->ds->linesize;
+       }
+    }
+}
+
+static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
+{
+    int i;
+
+    vs->has_hextile = 0;
+    vs->has_resize = 0;
+    vs->ds->dpy_copy = NULL;
+
+    for (i = n_encodings - 1; i >= 0; i--) {
+       switch (encodings[i]) {
+       case 0: /* Raw */
+           vs->has_hextile = 0;
+           break;
+       case 1: /* CopyRect */
+           vs->ds->dpy_copy = vnc_copy;
+           break;
+       case 5: /* Hextile */
+           vs->has_hextile = 1;
+           break;
+       case -223: /* DesktopResize */
+           vs->has_resize = 1;
+           break;
+       default:
+           break;
+       }
+    }
+}
+
+static void set_pixel_format(VncState *vs,
+                            int bits_per_pixel, int depth,
+                            int big_endian_flag, int true_color_flag,
+                            int red_max, int green_max, int blue_max,
+                            int red_shift, int green_shift, int blue_shift)
+{
+    switch (bits_per_pixel) {
+    case 32:
+    case 24:
+       vs->depth = 4;
+       break;
+    case 16:
+       vs->depth = 2;
+       break;
+    case 8:
+       vs->depth = 1;
+       break;
+    default:
+       vnc_client_error(vs);
+       break;
+    }
+
+    if (!true_color_flag)
+       vnc_client_error(vs);
+
+    vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height);
+    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+    memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height);
+
+    vga_hw_invalidate();
+    vga_hw_update();
+}
+
+static int protocol_client_msg(VncState *vs, char *data, size_t len)
+{
+    int i;
+    uint16_t limit;
+
+    switch (data[0]) {
+    case 0:
+       if (len == 1)
+           return 20;
+
+       set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+                        read_u8(data, 6), read_u8(data, 7),
+                        read_u16(data, 8), read_u16(data, 10),
+                        read_u16(data, 12), read_u8(data, 14),
+                        read_u8(data, 15), read_u8(data, 16));
+       break;
+    case 2:
+       if (len == 1)
+           return 4;
+
+       if (len == 4)
+           return 4 + (read_u16(data, 2) * 4);
+
+       limit = read_u16(data, 2);
+       for (i = 0; i < limit; i++) {
+           int32_t val = read_s32(data, 4 + (i * 4));
+           memcpy(data + 4 + (i * 4), &val, sizeof(val));
+       }
+
+       set_encodings(vs, (int32_t *)(data + 4), limit);
+       break;
+    case 3:
+       if (len == 1)
+           return 10;
+
+       framebuffer_update_request(vs,
+                                  read_u8(data, 1), read_u16(data, 2), 
read_u16(data, 4),
+                                  read_u16(data, 6), read_u16(data, 8));
+       break;
+    case 4:
+       if (len == 1)
+           return 8;
+
+       key_event(vs, read_u8(data, 1), read_u32(data, 4));
+       break;
+    case 5:
+       if (len == 1)
+           return 6;
+
+       pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 
4));
+       break;
+    case 6:
+       if (len == 1)
+           return 8;
+
+       if (len == 8)
+           return 8 + read_u32(data, 4);
+
+       client_cut_text(vs, read_u32(data, 4), data + 8);
+       break;
+    default:
+       printf("Msg: %d\n", data[0]);
+       vnc_client_error(vs);
+       break;
+    }
+       
+    vnc_read_when(vs, protocol_client_msg, 1);
+    return 0;
+}
+
+static int protocol_client_init(VncState *vs, char *data, size_t len)
+{
+    char pad[3] = { 0, 0, 0 };
+
+    vs->width = vs->ds->width;
+    vs->height = vs->ds->height;
+    vnc_write_u16(vs, vs->ds->width);
+    vnc_write_u16(vs, vs->ds->height);
+
+    vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */
+    vnc_write_u8(vs, vs->depth * 8); /* depth */
+    vnc_write_u8(vs, 0);             /* big-endian-flag */
+    vnc_write_u8(vs, 1);             /* true-color-flag */
+    if (vs->depth == 4) {
+       vnc_write_u16(vs, 0xFF);     /* red-max */
+       vnc_write_u16(vs, 0xFF);     /* green-max */
+       vnc_write_u16(vs, 0xFF);     /* blue-max */
+       vnc_write_u8(vs, 16);        /* red-shift */
+       vnc_write_u8(vs, 8);         /* green-shift */
+       vnc_write_u8(vs, 0);         /* blue-shift */
+    } else if (vs->depth == 2) {
+       vnc_write_u16(vs, 31);       /* red-max */
+       vnc_write_u16(vs, 63);       /* green-max */
+       vnc_write_u16(vs, 31);       /* blue-max */
+       vnc_write_u8(vs, 11);        /* red-shift */
+       vnc_write_u8(vs, 5);         /* green-shift */
+       vnc_write_u8(vs, 0);         /* blue-shift */
+    } else if (vs->depth == 1) {
+       vnc_write_u16(vs, 3);        /* red-max */
+       vnc_write_u16(vs, 7);        /* green-max */
+       vnc_write_u16(vs, 3);        /* blue-max */
+       vnc_write_u8(vs, 5);         /* red-shift */
+       vnc_write_u8(vs, 2);         /* green-shift */
+       vnc_write_u8(vs, 0);         /* blue-shift */
+    }
+       
+    vnc_write(vs, pad, 3);           /* padding */
+
+    vnc_write_u32(vs, 4);        
+    vnc_write(vs, "QEMU", 4);
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_msg, 1);
+
+    return 0;
+}
+
+static int protocol_version(VncState *vs, char *version, size_t len)
+{
+    char local[13];
+    int maj, min;
+
+    memcpy(local, version, 12);
+    local[12] = 0;
+
+    if (sscanf(local, "RFB %03d.%03d\n", &maj, &min) != 2) {
+       vnc_client_error(vs);
+       return 0;
+    }
+
+    vnc_write_u32(vs, 1); /* None */
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_init, 1);
+
+    return 0;
+}
+
+static void vnc_listen_read(void *opaque)
+{
+    VncState *vs = opaque;
+    struct sockaddr_in addr;
+    socklen_t addrlen = sizeof(addr);
+
+    vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+    if (vs->csock != -1) {
+       fcntl(vs->csock, F_SETFL, O_NONBLOCK);
+       qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, opaque);
+       vnc_write(vs, "RFB 003.003\n", 12);
+       vnc_flush(vs);
+       vnc_read_when(vs, protocol_version, 12);
+       memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
+       memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+       vs->has_resize = 0;
+       vs->has_hextile = 0;
+       vs->ds->dpy_copy = NULL;
+    }
+}
+
+void vnc_display_init(DisplayState *ds, int display)
+{
+    struct sockaddr_in addr;
+    int reuse_addr, ret;
+    VncState *vs;
+
+    vs = qemu_mallocz(sizeof(VncState));
+    if (!vs)
+       exit(1);
+
+    ds->opaque = vs;
+
+    vs->lsock = -1;
+    vs->csock = -1;
+    vs->depth = 4;
+
+    vs->ds = ds;
+
+    if (!keyboard_layout)
+       keyboard_layout = "en-us";
+
+    vs->kbd_layout = init_keyboard_layout(keyboard_layout);
+    if (!vs->kbd_layout)
+       exit(1);
+
+    vs->lsock = socket(PF_INET, SOCK_STREAM, 0);
+    if (vs->lsock == -1) {
+       fprintf(stderr, "Could not create socket\n");
+       exit(1);
+    }
+
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(5900 + display);
+    memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
+
+    reuse_addr = 1;
+    ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR,
+                    &reuse_addr, sizeof(reuse_addr));
+    if (ret == -1) {
+       fprintf(stderr, "setsockopt() failed\n");
+       exit(1);
+    }
+
+    if (bind(vs->lsock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+       fprintf(stderr, "bind() failed\n");
+       exit(1);
+    }
+
+    if (listen(vs->lsock, 1) == -1) {
+       fprintf(stderr, "listen() failed\n");
+       exit(1);
+    }
+
+    ret = qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, 
NULL, vs);
+    if (ret == -1) {
+       exit(1);
+    }
+
+    vs->ds->data = NULL;
+    vs->ds->dpy_update = vnc_dpy_update;
+    vs->ds->dpy_resize = vnc_dpy_resize;
+    vs->ds->dpy_refresh = vnc_dpy_refresh;
+
+    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+
+    vnc_dpy_resize(vs->ds, 640, 400);
+}
diff -urN -x '*.[di]' a.hg/vnchextile.h vnc.hg/vnchextile.h
--- a.hg/vnchextile.h   1969-12-31 18:00:00.000000000 -0600
+++ vnc.hg/vnchextile.h 2006-04-28 00:58:59.000000000 -0500
@@ -0,0 +1,189 @@
+#define CONCAT_I(a, b) a ## b
+#define CONCAT(a, b) CONCAT_I(a, b)
+#define pixel_t CONCAT(uint, CONCAT(BPP, _t))
+
+static void CONCAT(send_hextile_tile_, BPP)(VncState *vs,
+                                           int x, int y, int w, int h,
+                                           pixel_t *last_bg, pixel_t *last_fg,
+                                           int *has_bg, int *has_fg)
+{
+    char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth);
+    pixel_t *irow = (pixel_t *)row;
+    int j, i;
+    pixel_t bg = 0;
+    pixel_t fg = 0;
+    int n_colors = 0;
+    int bg_count = 0;
+    int fg_count = 0;
+    int flags = 0;
+    uint8_t data[(sizeof(pixel_t) + 2) * 16 * 16];
+    int n_data = 0;
+    int n_subtiles = 0;
+
+    for (j = 0; j < h; j++) {
+       for (i = 0; i < w; i++) {
+           switch (n_colors) {
+           case 0:
+               bg = irow[i];
+               n_colors = 1;
+               break;
+           case 1:
+               if (irow[i] != bg) {
+                   fg = irow[i];
+                   n_colors = 2;
+               }
+               break;
+           case 2:
+               if (irow[i] != bg && irow[i] != fg) {
+                   n_colors = 3;
+               } else {
+                   if (irow[i] == bg)
+                       bg_count++;
+                   else if (irow[i] == fg)
+                       fg_count++;
+               }
+               break;
+           default:
+               break;
+           }
+       }
+       if (n_colors > 2)
+           break;
+       irow += vs->ds->linesize / sizeof(pixel_t);
+    }
+
+    if (n_colors > 1 && fg_count > bg_count) {
+       pixel_t tmp = fg;
+       fg = bg;
+       bg = tmp;
+    }
+
+    if (!*has_bg || *last_bg != bg) {
+       flags |= 0x02;
+       *has_bg = 1;
+       *last_bg = bg;
+    }
+
+    if (!*has_fg || *last_fg != fg) {
+       flags |= 0x04;
+       *has_fg = 1;
+       *last_fg = fg;
+    }
+
+    switch (n_colors) {
+    case 1:
+       n_data = 0;
+       break;
+    case 2:
+       flags |= 0x08;
+
+       irow = (pixel_t *)row;
+       
+       for (j = 0; j < h; j++) {
+           int min_x = -1;
+           for (i = 0; i < w; i++) {
+               if (irow[i] == fg) {
+                   if (min_x == -1)
+                       min_x = i;
+               } else if (min_x != -1) {
+                   hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+                   n_data += 2;
+                   n_subtiles++;
+                   min_x = -1;
+               }
+           }
+           if (min_x != -1) {
+               hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+               n_data += 2;
+               n_subtiles++;
+           }
+           irow += vs->ds->linesize / sizeof(pixel_t);
+       }
+       break;
+    case 3:
+       flags |= 0x18;
+
+       irow = (pixel_t *)row;
+
+       if (!*has_bg || *last_bg != bg)
+           flags |= 0x02;
+
+       for (j = 0; j < h; j++) {
+           int has_color = 0;
+           int min_x = -1;
+           pixel_t color;
+
+           for (i = 0; i < w; i++) {
+               if (!has_color) {
+                   if (irow[i] == bg)
+                       continue;
+                   color = irow[i];
+                   min_x = i;
+                   has_color = 1;
+               } else if (irow[i] != color) {
+                   has_color = 0;
+
+                   memcpy(data + n_data, &color, sizeof(color));
+                   hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, 
i - min_x, 1);
+                   n_data += 2 + sizeof(pixel_t);
+                   n_subtiles++;
+
+                   min_x = -1;
+                   if (irow[i] != bg) {
+                       color = irow[i];
+                       min_x = i;
+                       has_color = 1;
+                   }
+               }
+           }
+           if (has_color) {
+               memcpy(data + n_data, &color, sizeof(color));
+               hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - 
min_x, 1);
+               n_data += 2 + sizeof(pixel_t);
+               n_subtiles++;
+           }
+           irow += vs->ds->linesize / sizeof(pixel_t);
+       }
+
+       /* A SubrectsColoured subtile invalidates the foreground color */
+       *has_fg = 0;
+       if (n_data > (w * h * sizeof(pixel_t))) {
+           n_colors = 4;
+           flags = 0x01;
+           *has_bg = 0;
+
+           /* we really don't have to invalidate either the bg or fg
+              but we've lost the old values.  oh well. */
+       }
+    default:
+       break;
+    }
+
+    if (n_colors > 3) {
+       flags = 0x01;
+       *has_fg = 0;
+       *has_bg = 0;
+       n_colors = 4;
+    }
+
+    vnc_write_u8(vs, flags);
+    if (n_colors < 4) {
+       if (flags & 0x02)
+           vnc_write(vs, last_bg, sizeof(pixel_t));
+       if (flags & 0x04)
+           vnc_write(vs, last_fg, sizeof(pixel_t));
+       if (n_subtiles) {
+           vnc_write_u8(vs, n_subtiles);
+           vnc_write(vs, data, n_data);
+       }
+    } else {
+       for (j = 0; j < h; j++) {
+           vnc_write(vs, row, w * vs->depth);
+           row += vs->ds->linesize;
+       }
+    }
+}
+
+#undef pixel_t
+#undef CONCAT_I
+#undef CONCAT
diff -urN -x '*.[di]' a.hg/vnc_keysym.h vnc.hg/vnc_keysym.h
--- a.hg/vnc_keysym.h   1969-12-31 18:00:00.000000000 -0600
+++ vnc.hg/vnc_keysym.h 2006-04-23 12:31:00.000000000 -0500
@@ -0,0 +1,275 @@
+typedef struct {
+       const char* name;
+       int keysym;
+} name2keysym_t;
+static name2keysym_t name2keysym[]={
+/* ascii */
+    { "space",                0x020},
+    { "exclam",               0x021},
+    { "quotedbl",             0x022},
+    { "numbersign",           0x023},
+    { "dollar",               0x024},
+    { "percent",              0x025},
+    { "ampersand",            0x026},
+    { "apostrophe",           0x027},
+    { "parenleft",            0x028},
+    { "parenright",           0x029},
+    { "asterisk",             0x02a},
+    { "plus",                 0x02b},
+    { "comma",                0x02c},
+    { "minus",                0x02d},
+    { "period",               0x02e},
+    { "slash",                0x02f},
+    { "0",                    0x030},
+    { "1",                    0x031},
+    { "2",                    0x032},
+    { "3",                    0x033},
+    { "4",                    0x034},
+    { "5",                    0x035},
+    { "6",                    0x036},
+    { "7",                    0x037},
+    { "8",                    0x038},
+    { "9",                    0x039},
+    { "colon",                0x03a},
+    { "semicolon",            0x03b},
+    { "less",                 0x03c},
+    { "equal",                0x03d},
+    { "greater",              0x03e},
+    { "question",             0x03f},
+    { "at",                   0x040},
+    { "A",                    0x041},
+    { "B",                    0x042},
+    { "C",                    0x043},
+    { "D",                    0x044},
+    { "E",                    0x045},
+    { "F",                    0x046},
+    { "G",                    0x047},
+    { "H",                    0x048},
+    { "I",                    0x049},
+    { "J",                    0x04a},
+    { "K",                    0x04b},
+    { "L",                    0x04c},
+    { "M",                    0x04d},
+    { "N",                    0x04e},
+    { "O",                    0x04f},
+    { "P",                    0x050},
+    { "Q",                    0x051},
+    { "R",                    0x052},
+    { "S",                    0x053},
+    { "T",                    0x054},
+    { "U",                    0x055},
+    { "V",                    0x056},
+    { "W",                    0x057},
+    { "X",                    0x058},
+    { "Y",                    0x059},
+    { "Z",                    0x05a},
+    { "bracketleft",          0x05b},
+    { "backslash",            0x05c},
+    { "bracketright",         0x05d},
+    { "asciicircum",          0x05e},
+    { "underscore",           0x05f},
+    { "grave",                0x060},
+    { "a",                    0x061},
+    { "b",                    0x062},
+    { "c",                    0x063},
+    { "d",                    0x064},
+    { "e",                    0x065},
+    { "f",                    0x066},
+    { "g",                    0x067},
+    { "h",                    0x068},
+    { "i",                    0x069},
+    { "j",                    0x06a},
+    { "k",                    0x06b},
+    { "l",                    0x06c},
+    { "m",                    0x06d},
+    { "n",                    0x06e},
+    { "o",                    0x06f},
+    { "p",                    0x070},
+    { "q",                    0x071},
+    { "r",                    0x072},
+    { "s",                    0x073},
+    { "t",                    0x074},
+    { "u",                    0x075},
+    { "v",                    0x076},
+    { "w",                    0x077},
+    { "x",                    0x078},
+    { "y",                    0x079},
+    { "z",                    0x07a},
+    { "braceleft",            0x07b},
+    { "bar",                  0x07c},
+    { "braceright",           0x07d},
+    { "asciitilde",           0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace",         0x0a0},
+{ "exclamdown",           0x0a1},
+{ "cent",                0x0a2},
+{ "sterling",             0x0a3},
+{ "currency",             0x0a4},
+{ "yen",                  0x0a5},
+{ "brokenbar",            0x0a6},
+{ "section",              0x0a7},
+{ "diaeresis",            0x0a8},
+{ "copyright",            0x0a9},
+{ "ordfeminine",          0x0aa},
+{ "guillemotleft",        0x0ab},
+{ "notsign",              0x0ac},
+{ "hyphen",               0x0ad},
+{ "registered",           0x0ae},
+{ "macron",               0x0af},
+{ "degree",               0x0b0},
+{ "plusminus",            0x0b1},
+{ "twosuperior",          0x0b2},
+{ "threesuperior",        0x0b3},
+{ "acute",                0x0b4},
+{ "mu",                   0x0b5},
+{ "paragraph",            0x0b6},
+{ "periodcentered",       0x0b7},
+{ "cedilla",              0x0b8},
+{ "onesuperior",          0x0b9},
+{ "masculine",            0x0ba},
+{ "guillemotright",       0x0bb},
+{ "onequarter",           0x0bc},
+{ "onehalf",              0x0bd},
+{ "threequarters",        0x0be},
+{ "questiondown",         0x0bf},
+{ "Agrave",               0x0c0},
+{ "Aacute",               0x0c1},
+{ "Acircumflex",          0x0c2},
+{ "Atilde",               0x0c3},
+{ "Adiaeresis",           0x0c4},
+{ "Aring",                0x0c5},
+{ "AE",                   0x0c6},
+{ "Ccedilla",             0x0c7},
+{ "Egrave",               0x0c8},
+{ "Eacute",               0x0c9},
+{ "Ecircumflex",          0x0ca},
+{ "Ediaeresis",           0x0cb},
+{ "Igrave",               0x0cc},
+{ "Iacute",               0x0cd},
+{ "Icircumflex",          0x0ce},
+{ "Idiaeresis",           0x0cf},
+{ "ETH",                  0x0d0},
+{ "Eth",                  0x0d0},
+{ "Ntilde",               0x0d1},
+{ "Ograve",               0x0d2},
+{ "Oacute",               0x0d3},
+{ "Ocircumflex",          0x0d4},
+{ "Otilde",               0x0d5},
+{ "Odiaeresis",           0x0d6},
+{ "multiply",             0x0d7},
+{ "Ooblique",             0x0d8},
+{ "Oslash",               0x0d8},
+{ "Ugrave",               0x0d9},
+{ "Uacute",               0x0da},
+{ "Ucircumflex",          0x0db},
+{ "Udiaeresis",           0x0dc},
+{ "Yacute",               0x0dd},
+{ "THORN",                0x0de},
+{ "Thorn",                0x0de},
+{ "ssharp",               0x0df},
+{ "agrave",               0x0e0},
+{ "aacute",               0x0e1},
+{ "acircumflex",          0x0e2},
+{ "atilde",               0x0e3},
+{ "adiaeresis",           0x0e4},
+{ "aring",                0x0e5},
+{ "ae",                   0x0e6},
+{ "ccedilla",             0x0e7},
+{ "egrave",               0x0e8},
+{ "eacute",               0x0e9},
+{ "ecircumflex",          0x0ea},
+{ "ediaeresis",           0x0eb},
+{ "igrave",               0x0ec},
+{ "iacute",               0x0ed},
+{ "icircumflex",          0x0ee},
+{ "idiaeresis",           0x0ef},
+{ "eth",                  0x0f0},
+{ "ntilde",               0x0f1},
+{ "ograve",               0x0f2},
+{ "oacute",               0x0f3},
+{ "ocircumflex",          0x0f4},
+{ "otilde",               0x0f5},
+{ "odiaeresis",           0x0f6},
+{ "division",             0x0f7},
+{ "oslash",               0x0f8},
+{ "ooblique",             0x0f8},
+{ "ugrave",               0x0f9},
+{ "uacute",               0x0fa},
+{ "ucircumflex",          0x0fb},
+{ "udiaeresis",           0x0fc},
+{ "yacute",               0x0fd},
+{ "thorn",                0x0fe},
+{ "ydiaeresis",           0x0ff},
+{"EuroSign", 0x20ac},  /* XK_EuroSign */
+
+    /* modifiers */
+{"Control_L", 0xffe3}, /* XK_Control_L */
+{"Control_R", 0xffe4}, /* XK_Control_R */
+{"Alt_L", 0xffe9},     /* XK_Alt_L */
+{"Alt_R", 0xffea},     /* XK_Alt_R */
+{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */
+{"Meta_L", 0xffe7},    /* XK_Meta_L */
+{"Meta_R", 0xffe8},    /* XK_Meta_R */
+{"Shift_L", 0xffe1},   /* XK_Shift_L */
+{"Shift_R", 0xffe2},   /* XK_Shift_R */
+{"Super_L", 0xffeb},   /* XK_Super_L */
+{"Super_R", 0xffec},   /* XK_Super_R */
+
+    /* special keys */
+{"BackSpace", 0xff08}, /* XK_BackSpace */
+{"Tab", 0xff09},       /* XK_Tab */
+{"Return", 0xff0d},    /* XK_Return */
+{"Right", 0xff53},     /* XK_Right */
+{"Left", 0xff51},      /* XK_Left */
+{"Up", 0xff52},        /* XK_Up */
+{"Down", 0xff54},      /* XK_Down */
+{"Page_Down", 0xff56}, /* XK_Page_Down */
+{"Page_Up", 0xff55},   /* XK_Page_Up */
+{"Insert", 0xff63},    /* XK_Insert */
+{"Delete", 0xffff},    /* XK_Delete */
+{"Home", 0xff50},      /* XK_Home */
+{"End", 0xff57},       /* XK_End */
+{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"F1", 0xffbe},        /* XK_F1 */
+{"F2", 0xffbf},        /* XK_F2 */
+{"F3", 0xffc0},        /* XK_F3 */
+{"F4", 0xffc1},        /* XK_F4 */
+{"F5", 0xffc2},        /* XK_F5 */
+{"F6", 0xffc3},        /* XK_F6 */
+{"F7", 0xffc4},        /* XK_F7 */
+{"F8", 0xffc5},        /* XK_F8 */
+{"F9", 0xffc6},        /* XK_F9 */
+{"F10", 0xffc7},       /* XK_F10 */
+{"F11", 0xffc8},       /* XK_F11 */
+{"F12", 0xffc9},       /* XK_F12 */
+{"F13", 0xffca},       /* XK_F13 */
+{"F14", 0xffcb},       /* XK_F14 */
+{"F15", 0xffcc},       /* XK_F15 */
+{"Sys_Req", 0xff15},   /* XK_Sys_Req */
+{"KP_0", 0xffb0},      /* XK_KP_0 */
+{"KP_1", 0xffb1},      /* XK_KP_1 */
+{"KP_2", 0xffb2},      /* XK_KP_2 */
+{"KP_3", 0xffb3},      /* XK_KP_3 */
+{"KP_4", 0xffb4},      /* XK_KP_4 */
+{"KP_5", 0xffb5},      /* XK_KP_5 */
+{"KP_6", 0xffb6},      /* XK_KP_6 */
+{"KP_7", 0xffb7},      /* XK_KP_7 */
+{"KP_8", 0xffb8},      /* XK_KP_8 */
+{"KP_9", 0xffb9},      /* XK_KP_9 */
+{"KP_Add", 0xffab},    /* XK_KP_Add */
+{"KP_Decimal", 0xffae},  /* XK_KP_Decimal */
+{"KP_Divide", 0xffaf},   /* XK_KP_Divide */
+{"KP_Enter", 0xff8d},    /* XK_KP_Enter */
+{"KP_Equal", 0xffbd},    /* XK_KP_Equal */
+{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */
+{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */
+{"help", 0xff6a},        /* XK_Help */
+{"Menu", 0xff67},        /* XK_Menu */
+{"Print", 0xff61},       /* XK_Print */
+{"Mode_switch", 0xff7e}, /* XK_Mode_switch */
+{"Num_Lock", 0xff7f},    /* XK_Num_Lock */
+{"Pause", 0xff13},       /* XK_Pause */
+{"Escape", 0xff1b},      /* XK_Escape */
+{0,0},
+};

reply via email to

[Prev in Thread] Current Thread [Next in Thread]