[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 2/2] PV framebuffer
PV framebuffer frontend. Derived from http://hg.codemonkey.ws/vncfb Extensive changes based on feedback from xen-devel. Signed-off-by: Markus Armbruster <armbru@xxxxxxxxxx> --- tools/Makefile | 1 tools/python/xen/xend/XendDevices.py | 4 tools/python/xen/xend/server/vfbif.py | 29 + tools/xenfb/Makefile | 33 + tools/xenfb/sdlfb.c | 337 ++++++++++++++++++++ tools/xenfb/vncfb.c | 396 +++++++++++++++++++++++ tools/xenfb/xenfb.c | 571 ++++++++++++++++++++++++++++++++++ tools/xenfb/xenfb.h | 34 ++ 8 files changed, 1404 insertions(+), 1 deletion(-) diff -r 2e35cf028ff0 tools/Makefile --- a/tools/Makefile Thu Nov 09 15:43:24 2006 +0000 +++ b/tools/Makefile Fri Nov 10 08:01:00 2006 +0100 @@ -19,6 +19,7 @@ SUBDIRS-y += libaio SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-y += xenfb # These don't cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff -r 2e35cf028ff0 tools/python/xen/xend/XendDevices.py --- a/tools/python/xen/xend/XendDevices.py Thu Nov 09 15:43:24 2006 +0000 +++ b/tools/python/xen/xend/XendDevices.py Thu Nov 09 17:58:26 2006 +0100 @@ -19,7 +19,7 @@ # A collection of DevControllers # -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif from xen.xend.server.BlktapController import BlktapController class XendDevices: @@ -41,6 +41,8 @@ class XendDevices: 'irq': irqif.IRQController, 'usb': usbif.UsbifController, 'tap': BlktapController, + 'vfb': vfbif.VfbifController, + 'vkbd': vfif.VkbdifController, } #@classmethod diff -r 2e35cf028ff0 tools/python/xen/xend/server/vfbif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/server/vfbif.py Tue Nov 07 14:46:46 2006 +0100 @@ -0,0 +1,29 @@ +from xen.xend.server.DevController import DevController + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) diff -r 2e35cf028ff0 tools/xenfb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Thu Nov 09 10:19:26 2006 +0100 @@ -0,0 +1,33 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include +LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +INSTALL = install +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +.PHONY: all +all: build + +.PHONY: build +build: mk-symlinks + $(MAKE) vncfb sdlfb + +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin + $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb + $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb + +sdlfb: sdlfb.o xenfb.o + +sdlfb.o: CFLAGS += $(shell sdl-config --cflags) +sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore + +clean: + $(RM) *.o *~ vncfb sdlfb + +vncfb: vncfb.o xenfb.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore diff -r 2e35cf028ff0 tools/xenfb/sdlfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Wed Nov 08 11:35:54 2006 +0100 @@ -0,0 +1,337 @@ +#include <SDL.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/select.h> +#include <stdlib.h> +#include <linux/input.h> +#include <getopt.h> +#include <string.h> +#include "xenfb.h" + +struct SDLFBData +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +/* + * Map from scancode to Linux input layer keycode. Scancodes are + * hardware-specific. This map assumes a standard AT or PS/2 + * keyboard. + * + * Why use scancodes? We can't use key symbols, because they don't + * identify keys --- they're what keys are mapped to. The standard + * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to + * SDLK_LESS. + */ +static int keymap[256] = { + [9] = KEY_ESC, + [10] = KEY_1, + [11] = KEY_2, + [12] = KEY_3, + [13] = KEY_4, + [14] = KEY_5, + [15] = KEY_6, + [16] = KEY_7, + [17] = KEY_8, + [18] = KEY_9, + [19] = KEY_0, + [20] = KEY_MINUS, + [21] = KEY_EQUAL, + [22] = KEY_BACKSPACE, + [23] = KEY_TAB, + [24] = KEY_Q, + [25] = KEY_W, + [26] = KEY_E, + [27] = KEY_R, + [28] = KEY_T, + [29] = KEY_Y, + [30] = KEY_U, + [31] = KEY_I, + [32] = KEY_O, + [33] = KEY_P, + [34] = KEY_LEFTBRACE, + [35] = KEY_RIGHTBRACE, + [36] = KEY_ENTER, + [37] = KEY_LEFTCTRL, + [38] = KEY_A, + [39] = KEY_S, + [40] = KEY_D, + [41] = KEY_F, + [42] = KEY_G, + [43] = KEY_H, + [44] = KEY_J, + [45] = KEY_K, + [46] = KEY_L, + [47] = KEY_SEMICOLON, + [48] = KEY_APOSTROPHE, + [49] = KEY_GRAVE, + [50] = KEY_LEFTSHIFT, + [51] = KEY_BACKSLASH, + [52] = KEY_Z, + [53] = KEY_X, + [54] = KEY_C, + [55] = KEY_V, + [56] = KEY_B, + [57] = KEY_N, + [58] = KEY_M, + [59] = KEY_COMMA, + [60] = KEY_DOT, + [61] = KEY_SLASH, + [62] = KEY_RIGHTSHIFT, + [63] = KEY_KPASTERISK, + [64] = KEY_LEFTALT, + [65] = KEY_SPACE, + [66] = KEY_CAPSLOCK, + [67] = KEY_F1, + [68] = KEY_F2, + [69] = KEY_F3, + [70] = KEY_F4, + [71] = KEY_F5, + [72] = KEY_F6, + [73] = KEY_F7, + [74] = KEY_F8, + [75] = KEY_F9, + [76] = KEY_F10, + [77] = KEY_NUMLOCK, + [78] = KEY_SCROLLLOCK, + [79] = KEY_KP7, + [80] = KEY_KP8, + [81] = KEY_KP9, + [82] = KEY_KPMINUS, + [83] = KEY_KP4, + [84] = KEY_KP5, + [85] = KEY_KP6, + [86] = KEY_KPPLUS, + [87] = KEY_KP1, + [88] = KEY_KP2, + [89] = KEY_KP3, + [90] = KEY_KP0, + [91] = KEY_KPDOT, + [94] = KEY_102ND, /* FIXME is this correct? */ + [95] = KEY_F11, + [96] = KEY_F12, + [108] = KEY_KPENTER, + [109] = KEY_RIGHTCTRL, + [112] = KEY_KPSLASH, + [111] = KEY_SYSRQ, + [113] = KEY_RIGHTALT, + [97] = KEY_HOME, + [98] = KEY_UP, + [99] = KEY_PAGEUP, + [100] = KEY_LEFT, + [102] = KEY_RIGHT, + [103] = KEY_END, + [104] = KEY_DOWN, + [105] = KEY_PAGEDOWN, + [106] = KEY_INSERT, + [107] = KEY_DELETE, + [110] = KEY_PAUSE, + [115] = KEY_LEFTMETA, + [116] = KEY_RIGHTMETA, + [117] = KEY_MENU, +}; + +static int btnmap[] = { + [SDL_BUTTON_LEFT] = BTN_LEFT, + [SDL_BUTTON_MIDDLE] = BTN_MIDDLE, + [SDL_BUTTON_RIGHT] = BTN_RIGHT, + /* FIXME not 100% sure about these: */ + [SDL_BUTTON_WHEELUP] = BTN_FORWARD, + [SDL_BUTTON_WHEELDOWN] BTN_BACK +}; + +static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct SDLFBData *data = xenfb->user_data; + SDL_Rect r = { x, y, width, height }; + SDL_BlitSurface(data->src, &r, data->dst, &r); + SDL_UpdateRect(data->dst, x, y, width, height); +} + +static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event) +{ + int x, y, ret; + + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (keymap[event->key.keysym.scancode] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_KEYDOWN, + keymap[event->key.keysym.scancode]); + if (ret < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + keymap[event->key.keysym.scancode], + event->type == SDL_KEYDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_MOUSEMOTION: + if (xenfb->abs_pointer_wanted) { + SDL_GetMouseState(&x, &y); + ret = xenfb_send_position(xenfb, x, y); + } else { + SDL_GetRelativeMouseState(&x, &y); + ret = xenfb_send_motion(xenfb, x, y); + } + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap)) + break; + if (btnmap[event->button.button] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_MOUSEBUTTONDOWN, + btnmap[event->button.button]); + if (ret < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + btnmap[event->button.button] - BTN_MOUSE, + event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_QUIT: + return 0; + } + + return 1; +} + +static struct option options[] = { + { "domid", 1, NULL, 'd' }, + { "title", 1, NULL, 't' }, +}; + +int main(int argc, char **argv) +{ + struct xenfb *xenfb; + int fd; + int domid = -1; + char * title = NULL; + fd_set readfds; + struct SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case 'd': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case 't': + title = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Could not initialize SDL\n"); + exit(1); + } + + fd = xenfb_get_fileno(xenfb); + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + if (!data.dst) { + fprintf(stderr, "SDL_SetVideoMode failed\n"); + exit(1); + } + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + if (!data.src) { + fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n"); + exit(1); + } + + if (title == NULL) + title = strdup("xen-sdlfb"); + SDL_WM_SetCaption(title, title); + + r.x = r.y = 0; + r.w = xenfb->width; + r.h = xenfb->height; + SDL_BlitSurface(data.src, &r, data.dst, &r); + SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height); + + xenfb->update = sdl_update; + xenfb->user_data = &data; + + SDL_ShowCursor(0); + + /* + * We need to wait for fd becoming ready or SDL events to + * arrive. We time out the select after 10ms to poll for SDL + * events. Clunky, but works. Could avoid the clunkiness + * with a separate thread. + */ + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + tv = (struct timeval){0, 10000}; + + if (select(fd + 1, &readfds, NULL, NULL, &tv) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can't select() on event channel (%s)\n", + strerror(errno)); + break; + } + + while (SDL_PollEvent(&event)) { + if (!sdl_on_event(xenfb, &event)) + do_quit = 1; + } + + if (do_quit) + break; + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff -r 2e35cf028ff0 tools/xenfb/vncfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/vncfb.c Thu Nov 09 10:18:58 2006 +0100 @@ -0,0 +1,396 @@ +#define _GNU_SOURCE +#include <errno.h> +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <malloc.h> +#include <rfb/rfb.h> +#include <rfb/keysym.h> +#include <linux/input.h> +#include <xs.h> +#include "xenfb.h" + +static int xk2linux[0x10000] = { + [XK_a] = KEY_A, + [XK_b] = KEY_B, + [XK_c] = KEY_C, + [XK_d] = KEY_D, + [XK_e] = KEY_E, + [XK_f] = KEY_F, + [XK_g] = KEY_G, + [XK_h] = KEY_H, + [XK_i] = KEY_I, + [XK_j] = KEY_J, + [XK_k] = KEY_K, + [XK_l] = KEY_L, + [XK_m] = KEY_M, + [XK_n] = KEY_N, + [XK_o] = KEY_O, + [XK_p] = KEY_P, + [XK_q] = KEY_Q, + [XK_r] = KEY_R, + [XK_s] = KEY_S, + [XK_t] = KEY_T, + [XK_u] = KEY_U, + [XK_v] = KEY_V, + [XK_w] = KEY_W, + [XK_x] = KEY_X, + [XK_y] = KEY_Y, + [XK_z] = KEY_Z, + [XK_A] = KEY_A, + [XK_B] = KEY_B, + [XK_C] = KEY_C, + [XK_D] = KEY_D, + [XK_E] = KEY_E, + [XK_F] = KEY_F, + [XK_G] = KEY_G, + [XK_H] = KEY_H, + [XK_I] = KEY_I, + [XK_J] = KEY_J, + [XK_K] = KEY_K, + [XK_L] = KEY_L, + [XK_M] = KEY_M, + [XK_N] = KEY_N, + [XK_O] = KEY_O, + [XK_P] = KEY_P, + [XK_Q] = KEY_Q, + [XK_R] = KEY_R, + [XK_S] = KEY_S, + [XK_T] = KEY_T, + [XK_U] = KEY_U, + [XK_V] = KEY_V, + [XK_W] = KEY_W, + [XK_X] = KEY_X, + [XK_Y] = KEY_Y, + [XK_Z] = KEY_Z, + [XK_0] = KEY_0, + [XK_1] = KEY_1, + [XK_2] = KEY_2, + [XK_3] = KEY_3, + [XK_4] = KEY_4, + [XK_5] = KEY_5, + [XK_6] = KEY_6, + [XK_7] = KEY_7, + [XK_8] = KEY_8, + [XK_9] = KEY_9, + [XK_Return] = KEY_ENTER, + [XK_BackSpace] = KEY_BACKSPACE, + [XK_Tab] = KEY_TAB, + [XK_Pause] = KEY_PAUSE, + [XK_Delete] = KEY_DELETE, + [XK_slash] = KEY_SLASH, + [XK_minus] = KEY_MINUS, + [XK_equal] = KEY_EQUAL, + [XK_Escape] = KEY_ESC, + [XK_braceleft] = KEY_LEFTBRACE, + [XK_braceright] = KEY_RIGHTBRACE, + [XK_bracketleft] = KEY_LEFTMETA, + [XK_bracketright] = KEY_RIGHTMETA, + [XK_Control_L] = KEY_LEFTCTRL, + [XK_Control_R] = KEY_RIGHTCTRL, + [XK_Shift_L] = KEY_LEFTSHIFT, + [XK_Shift_R] = KEY_RIGHTSHIFT, + [XK_Alt_L] = KEY_LEFTALT, + [XK_Alt_R] = KEY_RIGHTALT, + [XK_semicolon] = KEY_SEMICOLON, + [XK_apostrophe] = KEY_APOSTROPHE, + [XK_grave] = KEY_GRAVE, + [XK_backslash] = KEY_BACKSLASH, + [XK_comma] = KEY_COMMA, + [XK_period] = KEY_DOT, + [XK_space] = KEY_SPACE, + [XK_Caps_Lock] = KEY_CAPSLOCK, + [XK_Num_Lock] = KEY_NUMLOCK, + [XK_Scroll_Lock] = KEY_SCROLLLOCK, + [XK_Sys_Req] = KEY_SYSRQ, + [XK_Linefeed] = KEY_LINEFEED, + [XK_Home] = KEY_HOME, + [XK_Pause] = KEY_PAUSE, + [XK_F1] = KEY_F1, + [XK_F2] = KEY_F2, + [XK_F3] = KEY_F3, + [XK_F4] = KEY_F4, + [XK_F5] = KEY_F5, + [XK_F6] = KEY_F6, + [XK_F7] = KEY_F7, + [XK_F8] = KEY_F8, + [XK_F9] = KEY_F9, + [XK_F10] = KEY_F10, + [XK_F11] = KEY_F11, + [XK_F12] = KEY_F12, + [XK_Up] = KEY_UP, + [XK_Page_Up] = KEY_PAGEUP, + [XK_Left] = KEY_LEFT, + [XK_Right] = KEY_RIGHT, + [XK_End] = KEY_END, + [XK_Down] = KEY_DOWN, + [XK_Page_Down] = KEY_PAGEDOWN, + [XK_Insert] = KEY_INSERT, + [XK_colon] = KEY_SEMICOLON, + [XK_quotedbl] = KEY_APOSTROPHE, + [XK_less] = KEY_COMMA, + [XK_greater] = KEY_DOT, + [XK_question] = KEY_SLASH, + [XK_bar] = KEY_BACKSLASH, + [XK_asciitilde] = KEY_GRAVE, + [XK_exclam] = KEY_1, + [XK_at] = KEY_2, + [XK_numbersign] = KEY_3, + [XK_dollar] = KEY_4, + [XK_percent] = KEY_5, + [XK_asciicircum] = KEY_6, + [XK_ampersand] = KEY_7, + [XK_asterisk] = KEY_8, + [XK_parenleft] = KEY_9, + [XK_parenright] = KEY_0, + [XK_underscore] = KEY_MINUS, + [XK_plus] = KEY_EQUAL, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + /* + * We need to map to the key's Linux input layer keycode. + * Unfortunately, we don't get the key here, only the + * rfbKeySym, which is what the key is mapped to. Mapping + * back to the key is impossible in general, even when you + * know the keymap. For instance, the standard German keymap + * maps both KEY_COMMA and KEY_102ND to XK_less. We simply + * assume standard US layout. This sucks. + */ + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux)) + return; + if (xk2linux[keycode] == 0) + return; + if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + xk2linux[keycode], down ? "down" : "up", + strerror(errno)); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + /* initial pointer state: at (0,0), buttons up */ + static int last_x, last_y, last_button; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + int i, last_down, down, ret; + + for (i = 0; i < 8; i++) { + last_down = last_button & (1 << i); + down = buttonMask & (1 << i); + if (down == last_down) + continue; + /* FIXME this assumes buttons are numbered the same; verify they are */ + if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + i, down ? "down" : "up", strerror(errno)); + } + + if (x != last_x || y != last_y) { + if (xenfb->abs_pointer_wanted) + ret = xenfb_send_position(xenfb, x, y); + else + ret = xenfb_send_motion(xenfb, x - last_x, y - last_y); + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + } + + last_button = buttonMask; + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char portstr[10]; + struct xs_handle *xsh = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(stderr, "Can't get domain path (%s)\n", + strerror(errno)); + goto out; + } + + if (asprintf(&buf, "%s/console/vnc-port", path) == -1) { + fprintf(stderr, "Can't make vncport path\n"); + goto out; + } + + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Can't make vncport value\n"); + goto out; + } + + if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr))) + fprintf(stderr, "Can't set vncport (%s)\n", + strerror(errno)); + + out: + free(buf); +} + + +static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h) +{ + rfbScreenInfoPtr server = xenfb->user_data; + rfbMarkRectAsModified(server, x, y, x + w, y + h); +} + +static struct option options[] = { + { "domid", 1, NULL, 'd' }, + { "vncport", 1, NULL, 'p' }, + { "title", 1, NULL, 't' }, + { "unused", 0, NULL, 'u' }, + { "listen", 1, NULL, 'l' }, +}; + +int main(int argc, char **argv) +{ + rfbScreenInfoPtr server; + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", + "-desktop", "xen-vncfb", + "-listen", "0.0.0.0" }; + int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); + int domid = -1, port = -1; + char *title = NULL; + char *listen = NULL; + bool unused = false; + int opt; + struct xenfb *xenfb; + fd_set readfds; + int fd; + char portstr[10]; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case 'd': + errno = 0; + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case 'p': + errno = 0; + port = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + break; + case 't': + title = strdup(optarg); + break; + case 'u': + unused = true; + break; + case 'l': + listen = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port <= 0) + port = 5900 + domid; + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + + fake_argv[2] = portstr; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + signal(SIGPIPE, SIG_IGN); + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + server = rfbGetScreen(&fake_argc, fake_argv, + xenfb->width, xenfb->height, + 8, 3, xenfb->depth / 8); + if (server == NULL) { + fprintf(stderr, "Could not create VNC server\n"); + exit(1); + } + + xenfb->user_data = server; + xenfb->update = vnc_update; + + if (unused) + server->autoPort = true; + + server->serverFormat.redShift = 16; + server->serverFormat.greenShift = 8; + server->serverFormat.blueShift = 0; + server->kbdAddEvent = on_kbd_event; + server->ptrAddEvent = on_ptr_event; + server->frameBuffer = (char *)xenfb->pixels; + server->screenData = xenfb; + server->cursor = NULL; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, true); + + fd = xenfb_get_fileno(xenfb); + + xenstore_write_vncport(server->port, domid); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can't select() on event channel (%s)\n", + strerror(errno)); + break; + } + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff -r 2e35cf028ff0 tools/xenfb/xenfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Thu Nov 09 10:22:36 2006 +0100 @@ -0,0 +1,571 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/xenfb.h> +#include <xen/io/xenkbd.h> +#include <sys/select.h> +#include <stdbool.h> +#include <xen/linux/evtchn.h> +#include <xen/event_channel.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <xs.h> + +#include "xenfb.h" + +// FIXME defend against malicious frontend? + +struct xenfb_private +{ + struct xenfb pub; + int domid; + evtchn_port_t fbdev_port, kbd_port; + int evt_xch; + int xc; + unsigned char *fb; + size_t fb_len; + struct xenfb_page *fb_info; + struct xenkbd_page *kbd_info; +}; + +struct xenfb *xenfb_new(void) +{ + struct xenfb_private *xenfb = malloc(sizeof(*xenfb)); + + if (xenfb == NULL) + return NULL; + + memset(xenfb, 0, sizeof(*xenfb)); + + xenfb->domid = -1; + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) { + int serrno = errno; + free(xenfb); + errno = serrno; + return NULL; + } + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) { + int serrno = errno; + xc_evtchn_close(xenfb->evt_xch); + free(xenfb); + errno = serrno; + return NULL; + } + + return &xenfb->pub; +} + +int xenfb_get_fileno(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + return xc_evtchn_fd(xenfb->evt_xch); +} + +static void xenfb_detach_dom(struct xenfb_private *xenfb) +{ + xenfb->domid = -1; + munmap(xenfb->fb, xenfb->fb_len); + munmap(xenfb->fb_info, XC_PAGE_SIZE); + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); +} + +void xenfb_delete(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + if (xenfb->domid != -1) + xenfb_detach_dom(xenfb); + free(xenfb); +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *info = xenfb->kbd_info; + + prod = info->in_prod; + if (prod - info->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(info, prod) = *event; + wmb(); /* ensure ring contents visible */ + info->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = -ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = -ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = -EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = -ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = -ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, + unsigned awaited) +{ + int ret; + unsigned state, dummy; + char **vec; + + for (;;) { + ret = xenfb_xs_scanf1(xsh, dir, "state", "%u", &state); + if (ret < 0 && errno != -ENOENT) + return ret; + if (state > XenbusStateClosed) + state = XenbusStateUnknown; + if ((1 << state) & awaited) + return state; + + vec = xs_read_watch(xsh, &dummy); + if (!vec) + return -1; + free(vec); + } +} + +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) +{ + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) + return -1; + + if (!xs_watch(xsh, backdir, "")) + return -1; + + switch (xenfb_wait_for_state(xsh, backdir, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialising) + | (1 << XenbusStateInitWait) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialising, +#endif + )) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + break; + default: + return -1; + } + + xs_unwatch(xsh, backdir, ""); + return 0; +} + +static int xenfb_wait_for_frontend_initialised(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialised) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialised, +#endif + )) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_wait_for_frontend_connected(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, + 1 << XenbusStateConnected)) { + case XenbusStateConnected: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_get_connection(struct xs_handle *xsh, const char *frontdir, + unsigned long *mfn, int *evtchn) +{ + if (xenfb_xs_scanf1(xsh, frontdir, "page-ref", "%lu", mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xsh, frontdir, "event-channel", "%u", evtchn) + < 0) + return -1; + return 0; +} + +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + char vfb_frontdir[64], vfb_backdir[64]; + char vkbd_frontdir[64], vkbd_backdir[64]; + struct xs_handle *xsh; + int ret, val; + int n_fbmfns; + int n_fbdirs = 0; + unsigned long *fbmfns = NULL; + int serrno; + int fbdev_evtchn, kbd_evtchn; + unsigned long fbdev_mfn, kbd_mfn; + + if (xenfb->domid != -1) { + xenfb_detach_dom(xenfb); + if (domid == -1) + return true; + } + + xsh = xs_daemon_open(); + if (!xsh) + goto error; + + if (!xenfb_path_in_dom(xsh, vfb_frontdir, sizeof(vfb_frontdir), + domid, "device/vfb/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vfb_backdir, sizeof(vfb_backdir), + 0, "backend/vfb/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_frontdir, sizeof(vkbd_frontdir), + domid, "device/vkbd/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_backdir, sizeof(vkbd_backdir), + 0, "backend/vkbd/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + + if (xenfb_hotplug(xsh, vfb_backdir) < 0) + goto error; + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) + goto error; + + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) + goto error; + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + + if (!xs_watch(xsh, vfb_frontdir, "")) + goto error; + if (!xs_watch(xsh, vkbd_frontdir, "")) + goto error; + + if (xenfb_wait_for_frontend_initialised(xsh, vfb_frontdir) < 0) + goto error; + if (xenfb_wait_for_frontend_initialised(xsh, vkbd_frontdir) < 0) + goto error; + + if (xenfb_get_connection(xsh, vfb_frontdir, &fbdev_mfn, &fbdev_evtchn) + < 0) + goto error; + if (xenfb_get_connection(xsh, vkbd_frontdir, &kbd_mfn, &kbd_evtchn) + < 0) + goto error; + + if (xenfb_xs_scanf1(xsh, vfb_frontdir, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + errno = ENOTSUP; + goto error; + } + xenfb_xs_printf(xsh, vfb_backdir, "request-update", "1"); + + xenfb->fbdev_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + fbdev_evtchn); + if (xenfb->fbdev_port == -1) + goto error; + + xenfb->kbd_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + kbd_evtchn); + if (xenfb->kbd_port == -1) + goto error; + + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, 1, + &fbdev_mfn, &fbdev_mfn); + if (ret < 0 && errno != EINVAL) + goto error; + xenfb->fb_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + fbdev_mfn); + if (xenfb->fb_info == NULL) + goto error; + + xenfb->kbd_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + kbd_mfn); + if (xenfb->kbd_info == NULL) + goto error; + + /* TODO check for permitted ranges */ + xenfb->pub.depth = xenfb->fb_info->depth; + xenfb->pub.width = xenfb->fb_info->width; + xenfb->pub.height = xenfb->fb_info->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = xenfb->fb_info->mem_length; + xenfb->pub.row_stride = xenfb->fb_info->line_length; + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * sizeof(unsigned long); + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + /* + * Bug alert: xc_map_foreign_batch() can fail partly and + * return a non-null value. This is a design flaw. When it + * happens, we happily continue here, and later crash on + * access. + */ + fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ, xenfb->fb_info->pd, n_fbdirs); + if (fbmfns == NULL) + goto error; + + xenfb->fb = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->fb == NULL) + goto error; + + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + + if (xenfb_wait_for_frontend_connected(xsh, vkbd_frontdir) < 0) + goto error; + if (xenfb_xs_scanf1(xsh, vkbd_frontdir, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->pub.abs_pointer_wanted = val; + + xs_daemon_close(xsh); + xsh = NULL; + + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + + xenfb->domid = domid; + + xenfb->pub.pixels = xenfb->fb; + + return true; + + error: + serrno = errno; + if (xenfb->fb) + munmap(xenfb->fb, xenfb->fb_len); + if (fbmfns) + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + if (xenfb->kbd_info) + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + if (xenfb->fb_info) + munmap(xenfb->fb_info, XC_PAGE_SIZE); + if (xenfb->kbd_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); + if (xenfb->fbdev_port) + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + if (xsh) + xs_daemon_close(xsh); + errno = serrno; + return false; +} + +static void xenfb_on_fb_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *info = xenfb->fb_info; + + prod = info->out_prod; + if (prod == info->out_cons) + return; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = info->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(info, cons); + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, + event->update.x, event->update.y, + event->update.width, event->update.height); + break; + } + } + mb(); /* ensure we're done with ring contents */ + info->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fbdev_port); +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + struct xenkbd_page *info = xenfb->kbd_info; + + /* We don't understand any keyboard events, so just ignore them. */ + if (info->out_prod == info->out_cons) + return; + info->out_cons = info->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +int xenfb_on_incoming(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + evtchn_port_t port; + + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) + return -1; + + if (port == xenfb->fbdev_port) { + xenfb_on_fb_event(xenfb); + } else if (port == xenfb->kbd_port) { + xenfb_on_kbd_event(xenfb); + } + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) + return -1; + + return 0; +} + +int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + + return xenfb_kbd_event(xenfb, &event); +} diff -r 2e35cf028ff0 tools/xenfb/xenfb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Wed Nov 08 09:26:08 2006 +0100 @@ -0,0 +1,34 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include <stdbool.h> +#include <stdint.h> + +struct xenfb +{ + uint8_t *pixels; + + int row_stride; + int depth; + int width; + int height; + int abs_pointer_wanted; + + void *user_data; + + void (*update)(struct xenfb *xenfb, int x, int y, int width, int height); +}; + +struct xenfb *xenfb_new(void); +void xenfb_delete(struct xenfb *xenfb); + +bool xenfb_attach_dom(struct xenfb *xenfb, int domid); + +int xenfb_get_fileno(struct xenfb *xenfb); +int xenfb_on_incoming(struct xenfb *xenfb); + +int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); +int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y); +int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y); + +#endif _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |