--- a/tools/Makefile Sat Sep 02 15:11:17 2006 -0400 +++ b/tools/Makefile Sat Sep 02 15:19:25 2006 -0400 @@ -18,6 +18,7 @@ SUBDIRS-y += xenstat SUBDIRS-y += xenstat SUBDIRS-y += libaio SUBDIRS-y += blktap +SUBDIRS-y += xenfb # These don't cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Sat Sep 02 15:19:25 2006 -0400 @@ -0,0 +1,36 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -g -Wall +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 + +keymapping.o: CFLAGS += $(shell pkg-config --cflags gtk+-2.0) + +vncfb: vncfb.o xenfb.o keymapping.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/keymapping.c Sat Sep 02 15:19:25 2006 -0400 @@ -0,0 +1,141 @@ +#include +#include +#include + +uint32_t gdk_linux_mapping[0x10000] = { + [GDK_a] = KEY_A, + [GDK_b] = KEY_B, + [GDK_c] = KEY_C, + [GDK_d] = KEY_D, + [GDK_e] = KEY_E, + [GDK_f] = KEY_F, + [GDK_g] = KEY_G, + [GDK_h] = KEY_H, + [GDK_i] = KEY_I, + [GDK_j] = KEY_J, + [GDK_k] = KEY_K, + [GDK_l] = KEY_L, + [GDK_m] = KEY_M, + [GDK_n] = KEY_N, + [GDK_o] = KEY_O, + [GDK_p] = KEY_P, + [GDK_q] = KEY_Q, + [GDK_r] = KEY_R, + [GDK_s] = KEY_S, + [GDK_t] = KEY_T, + [GDK_u] = KEY_U, + [GDK_v] = KEY_V, + [GDK_w] = KEY_W, + [GDK_x] = KEY_X, + [GDK_y] = KEY_Y, + [GDK_z] = KEY_Z, + [GDK_A] = KEY_A, + [GDK_B] = KEY_B, + [GDK_C] = KEY_C, + [GDK_D] = KEY_D, + [GDK_E] = KEY_E, + [GDK_F] = KEY_F, + [GDK_G] = KEY_G, + [GDK_H] = KEY_H, + [GDK_I] = KEY_I, + [GDK_J] = KEY_J, + [GDK_K] = KEY_K, + [GDK_L] = KEY_L, + [GDK_M] = KEY_M, + [GDK_N] = KEY_N, + [GDK_O] = KEY_O, + [GDK_P] = KEY_P, + [GDK_Q] = KEY_Q, + [GDK_R] = KEY_R, + [GDK_S] = KEY_S, + [GDK_T] = KEY_T, + [GDK_U] = KEY_U, + [GDK_V] = KEY_V, + [GDK_W] = KEY_W, + [GDK_X] = KEY_X, + [GDK_Y] = KEY_Y, + [GDK_Z] = KEY_Z, + [GDK_0] = KEY_0, + [GDK_1] = KEY_1, + [GDK_2] = KEY_2, + [GDK_3] = KEY_3, + [GDK_4] = KEY_4, + [GDK_5] = KEY_5, + [GDK_6] = KEY_6, + [GDK_7] = KEY_7, + [GDK_8] = KEY_8, + [GDK_9] = KEY_9, + [GDK_Return] = KEY_ENTER, + [GDK_BackSpace] = KEY_BACKSPACE, + [GDK_Tab] = KEY_TAB, + [GDK_Pause] = KEY_PAUSE, + [GDK_Delete] = KEY_DELETE, + [GDK_slash] = KEY_SLASH, + [GDK_minus] = KEY_MINUS, + [GDK_equal] = KEY_EQUAL, + [GDK_Escape] = KEY_ESC, + [GDK_braceleft] = KEY_LEFTBRACE, + [GDK_braceright] = KEY_RIGHTBRACE, + [GDK_bracketleft] = KEY_LEFTMETA, + [GDK_bracketright] = KEY_RIGHTMETA, + [GDK_Control_L] = KEY_LEFTCTRL, + [GDK_Control_R] = KEY_RIGHTCTRL, + [GDK_Shift_L] = KEY_LEFTSHIFT, + [GDK_Shift_R] = KEY_RIGHTSHIFT, + [GDK_Alt_L] = KEY_LEFTALT, + [GDK_Alt_R] = KEY_RIGHTALT, + [GDK_semicolon] = KEY_SEMICOLON, + [GDK_apostrophe] = KEY_APOSTROPHE, + [GDK_grave] = KEY_GRAVE, + [GDK_backslash] = KEY_BACKSLASH, + [GDK_comma] = KEY_COMMA, + [GDK_period] = KEY_DOT, + [GDK_space] = KEY_SPACE, + [GDK_Caps_Lock] = KEY_CAPSLOCK, + [GDK_Num_Lock] = KEY_NUMLOCK, + [GDK_Scroll_Lock] = KEY_SCROLLLOCK, + [GDK_Sys_Req] = KEY_SYSRQ, + [GDK_Linefeed] = KEY_LINEFEED, + [GDK_Home] = KEY_HOME, + [GDK_Pause] = KEY_PAUSE, + [GDK_F1] = KEY_F1, + [GDK_F2] = KEY_F2, + [GDK_F3] = KEY_F3, + [GDK_F4] = KEY_F4, + [GDK_F5] = KEY_F5, + [GDK_F6] = KEY_F6, + [GDK_F7] = KEY_F7, + [GDK_F8] = KEY_F8, + [GDK_F9] = KEY_F9, + [GDK_F10] = KEY_F10, + [GDK_F11] = KEY_F11, + [GDK_F12] = KEY_F12, + [GDK_Up] = KEY_UP, + [GDK_Page_Up] = KEY_PAGEUP, + [GDK_Left] = KEY_LEFT, + [GDK_Right] = KEY_RIGHT, + [GDK_End] = KEY_END, + [GDK_Down] = KEY_DOWN, + [GDK_Page_Down] = KEY_PAGEDOWN, + [GDK_Insert] = KEY_INSERT, + [GDK_colon] = KEY_SEMICOLON, + [GDK_quotedbl] = KEY_APOSTROPHE, + [GDK_less] = KEY_COMMA, + [GDK_greater] = KEY_DOT, + [GDK_question] = KEY_SLASH, + [GDK_bar] = KEY_BACKSLASH, + [GDK_asciitilde] = KEY_GRAVE, + [GDK_exclam] = KEY_1, + [GDK_at] = KEY_2, + [GDK_numbersign] = KEY_3, + [GDK_dollar] = KEY_4, + [GDK_percent] = KEY_5, + [GDK_asciicircum] = KEY_6, + [GDK_ampersand] = KEY_7, + [GDK_asterisk] = KEY_8, + [GDK_parenleft] = KEY_9, + [GDK_parenright] = KEY_0, + [GDK_underscore] = KEY_MINUS, + [GDK_plus] = KEY_EQUAL, +}; + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Sat Sep 02 15:19:25 2006 -0400 @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include "xenfb.h" + +struct data +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct data *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); +} + +int sdl2linux[1024] = { + [SDLK_a] = KEY_A, + [SDLK_b] = KEY_B, + [SDLK_c] = KEY_C, + [SDLK_d] = KEY_D, + [SDLK_e] = KEY_E, + [SDLK_f] = KEY_F, + [SDLK_g] = KEY_G, + [SDLK_h] = KEY_H, + [SDLK_i] = KEY_I, + [SDLK_j] = KEY_J, + [SDLK_k] = KEY_K, + [SDLK_l] = KEY_L, + [SDLK_m] = KEY_M, + [SDLK_n] = KEY_N, + [SDLK_o] = KEY_O, + [SDLK_p] = KEY_P, + [SDLK_q] = KEY_Q, + [SDLK_r] = KEY_R, + [SDLK_s] = KEY_S, + [SDLK_t] = KEY_T, + [SDLK_u] = KEY_U, + [SDLK_v] = KEY_V, + [SDLK_w] = KEY_W, + [SDLK_x] = KEY_X, + [SDLK_y] = KEY_Y, + [SDLK_z] = KEY_Z, + [SDLK_0] = KEY_0, + [SDLK_1] = KEY_1, + [SDLK_2] = KEY_2, + [SDLK_3] = KEY_3, + [SDLK_4] = KEY_4, + [SDLK_5] = KEY_5, + [SDLK_6] = KEY_6, + [SDLK_7] = KEY_7, + [SDLK_8] = KEY_8, + [SDLK_9] = KEY_9, + [SDLK_SPACE] = KEY_SPACE, + [SDLK_RETURN] = KEY_ENTER, + [SDLK_PERIOD] = KEY_DOT, + [SDLK_SLASH] = KEY_SLASH, + [SDLK_BACKSPACE] = KEY_BACKSPACE, + [SDLK_TAB] = KEY_TAB, + [SDLK_LSHIFT] = KEY_LEFTSHIFT, + [SDLK_RSHIFT] = KEY_RIGHTSHIFT, + [SDLK_LALT] = KEY_LEFTALT, + [SDLK_RALT] = KEY_RIGHTALT, +}; + +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 data data; + SDL_Rect r; + struct timeval tv = { 0, 500 }; + int do_quit = 0; + int opt; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case 'd': + domid = strtol(optarg, NULL, 10); + break; + case 't': + title = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid == -1) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) + return 1; + + if (!xenfb_attach_dom(xenfb, domid)) + return 1; + + SDL_Init(SDL_INIT_VIDEO); + + fd = xenfb_get_fileno(xenfb); + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + 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; + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + SDL_ShowCursor(0); + + while (!do_quit && select(fd + 1, &readfds, NULL, NULL, &tv) != -1) { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + xenfb_send_key(xenfb, + event.type == SDL_KEYDOWN, + sdl2linux[event.key.keysym.sym]); + break; + case SDL_MOUSEMOTION: { + int x, y; + Uint8 button; + + button = SDL_GetRelativeMouseState(&x, &y); + xenfb_send_motion(xenfb, x, y); + } break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + xenfb_send_button(xenfb, + event.type==SDL_MOUSEBUTTONDOWN, + 3 - event.button.button); + break; + case SDL_QUIT: + do_quit = 1; + break; + } + } + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + tv = (struct timeval){0, 500}; + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} --- b/tools/xenfb/vncfb.c Sat Sep 02 15:19:25 2006 -0400 +++ b/tools/xenfb/vncfb.c Sat Sep 02 15:22:19 2006 -0400 @@ -0,0 +1,245 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "xenfb.h" + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + extern uint32_t gdk_linux_mapping[0x10000]; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + xenfb_send_key(xenfb, down, gdk_linux_mapping[keycode & 0xFFFF]); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + static int last_x = -1, last_y = -1; + static int last_button = -1; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + + if (last_button != -1) { + int i; + + for (i = 0; i < 8; i++) { + if ((last_button & (1 << i)) != + (buttonMask & (1 << i))) { + printf("%d %d\n", buttonMask & (1 << i), i); + xenfb_send_button(xenfb, buttonMask & (1 << i), + 2 - i); + } + } + } + + if (last_x != -1) + xenfb_send_motion(xenfb, x - last_x, y - last_y); + + last_button = buttonMask; + + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char *portstr = NULL; + 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, "xs_get_domain_path() error\n"); + goto out; + } + + buf = malloc(256); + if (snprintf(buf, 256, "%s/console/vnc-port", path) == -1) + goto out; + + portstr = malloc(10); + if (snprintf(portstr, 10, "%d", port) == -1) + goto out; + + if (xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)) == 0) + fprintf(stderr, "xs_write() vncport failed\n"); + + out: + free(portstr); + 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 int vnc_start_viewer(int port) +{ + int pid; + char s[16]; + + snprintf(s, 16, ":%d", port); + switch (pid = fork()) { + case -1: + fprintf(stderr, "vncviewer failed fork\n"); + exit(1); + + case 0: /* child */ + execlp("vncviewer", "vncviewer", s, 0); + fprintf(stderr, "vncviewer execlp failed\n"); + exit(1); + + default: + return pid; + } +} + +static struct option options[] = { + { "domid", 1, NULL, 'd' }, + { "vncport", 1, NULL, 'p' }, + { "title", 1, NULL, 't' }, + { "unused", 0, NULL, 'u' }, + { "listen", 1, NULL, 'l' }, + { "vncviewer", 0, NULL, 'v' }, +}; + +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; + struct xenfb *xenfb; + fd_set readfds; + int fd; + char buffer[1024]; + int opt; + bool unused = FALSE; + bool viewer = FALSE; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case 'd': + domid = strtol(optarg, NULL, 10); + break; + case 'p': + port = strtol(optarg, NULL, 10); + break; + case 't': + title = strdup(optarg); + break; + case 'u': + unused = TRUE; + break; + case 'l': + listen = strdup(optarg); + break; + case 'v': + viewer = TRUE; + break; + case 'l': + listen = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid == -1) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port == -1) + port = 5900 + domid; + snprintf(buffer, sizeof(buffer), "%d", port); + fake_argv[2] = buffer; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + if (listen != NULL) + fake_argv[6] = listen; + + 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; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, TRUE); + + fd = xenfb_get_fileno(xenfb); + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + xenstore_write_vncport(server->port, domid); + + if (viewer) + vnc_start_viewer(server->port); + + while (select(fd + 1, &readfds, NULL, NULL, NULL) != -1) { + if (FD_ISSET(fd, &readfds)) { + xenfb_on_incoming(xenfb); + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Sat Sep 02 15:19:25 2006 -0400 @@ -0,0 +1,434 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenfb.h" + +// FIXME defend against malicous backend? + +struct xenfb_private +{ + struct xenfb pub; + int domid; + unsigned long fbdev_mfn, kbd_mfn; + int fbdev_evtchn, kbd_evtchn; + evtchn_port_t fbdev_port, kbd_port; + int evt_xch; + int xc; + unsigned char *fb; + struct xenfb_page *fb_info; + struct xenkbd_info *kbd_info; + unsigned long *fbmfns; + int n_fbmfns, n_fbdirs; +}; + +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_info->mem_length); + 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_fb_event(struct xenfb_private *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *info = xenfb->fb_info; + + prod = info->in_prod; + if (prod - info->in_cons == XENFB_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(info, prod) = *event; + wmb(); /* ensure ring contents visible */ + info->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->fbdev_port); +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_info *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 *h, + unsigned domid, const char *path, + char *buffer, size_t size) +{ + char *domp = xs_get_domain_path(h, domid); + int n = snprintf(buffer, size, "%s/%s", domp, path); + free(domp); + if (n >= size) + return NULL; + return buffer; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, unsigned domid, + const char *path, const char *fmt, + void *dest) +{ + char buffer[1024]; + char *p; + int ret; + + p = xenfb_path_in_dom(xsh, domid, path, buffer, sizeof(buffer)); + p = xs_read(xsh, XBT_NULL, p, NULL); + if (!p) + return -ENOENT; + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) + return -EDOM; + return 0; +} + +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + char buffer[1024]; + struct xs_handle *xsh; + unsigned dummy; + int ret; + char *p, **vec; + union xenfb_in_event event; + + if (xenfb->domid != -1) { + xenfb_detach_dom(xenfb); + if (domid == -1) + return true; + } + + xsh = xs_daemon_open_readonly(); + if (!xsh) + goto error; + + p = xenfb_path_in_dom(xsh, domid, "vfb", buffer, sizeof(buffer)); + if (!xs_watch(xsh, p, "")) + goto error; + p = xenfb_path_in_dom(xsh, domid, "vkbd", buffer, sizeof(buffer)); + if (!xs_watch(xsh, p, "")) + goto error; + + for (;;) { + ret = xenfb_xs_scanf1(xsh, domid, "vfb/page-ref", "%lu", + &xenfb->fbdev_mfn); + if (ret == -ENOENT || ret == -EAGAIN) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vfb/event-channel", "%u", + &xenfb->fbdev_evtchn); + if (ret == -ENOENT || ret == -EAGAIN) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vkbd/page-ref", "%lu", + &xenfb->kbd_mfn); + if (ret == -ENOENT || ret == -EAGAIN) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vkbd/event-channel", "%u", + &xenfb->kbd_evtchn); + if (ret == -ENOENT || ret == -EAGAIN) + goto wait; + if (ret < 0) + goto error; + break; + + wait: + printf("Waiting...\n"); + vec = xs_read_watch(xsh, &dummy); + if (!vec) + goto error; + free(vec); + } + xs_daemon_close(xsh); + xsh = NULL; + + xenfb->fbdev_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + xenfb->fbdev_evtchn); + if (xenfb->fbdev_port == -1) + goto error; + + xenfb->kbd_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + xenfb->kbd_evtchn); + if (xenfb->kbd_port == -1) + goto error_fbdev; + + xenfb->fb_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + xenfb->fbdev_mfn); + if (xenfb->fb_info == NULL) + goto error_kbd; + + xenfb->kbd_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + xenfb->kbd_mfn); + if (xenfb->kbd_info == NULL) + goto error_fbinfo; + + xenfb->n_fbmfns = (xenfb->fb_info->mem_length + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + xenfb->n_fbdirs = xenfb->n_fbmfns * sizeof(unsigned long); + xenfb->n_fbdirs = (xenfb->n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + xenfb->fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ, xenfb->fb_info->pd, xenfb->n_fbdirs); + if (xenfb->fbmfns == NULL) + goto error_kbdinfo; + + xenfb->fb = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, xenfb->fbmfns, xenfb->n_fbmfns); + if (xenfb->fb == NULL) + goto error_fbmfns; + + event.type = XENFB_TYPE_SET_EVENTS; + event.set_events.flags = XENFB_FLAG_UPDATE; + if (xenfb_fb_event(xenfb, &event)) + goto error_fb; + + munmap(xenfb->fbmfns, xenfb->n_fbdirs * XC_PAGE_SIZE); + + xenfb->domid = domid; + + xenfb->pub.pixels = xenfb->fb; + + xenfb->pub.row_stride = xenfb->fb_info->line_length; + xenfb->pub.depth = xenfb->fb_info->depth; + xenfb->pub.width = xenfb->fb_info->width; + xenfb->pub.height = xenfb->fb_info->height; + + return true; + + error_fb: + printf("%d\n", __LINE__); + { + int serrno = errno; + munmap(xenfb->fb, xenfb->fb_info->mem_length); + errno = serrno; + } + error_fbmfns: + printf("%d\n", __LINE__); + { + int serrno = errno; + munmap(xenfb->fbmfns, xenfb->n_fbdirs * XC_PAGE_SIZE); + errno = serrno; + } + error_kbdinfo: + printf("%d\n", __LINE__); + { + int serrno = errno; + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + errno = serrno; + } + error_fbinfo: + printf("%d\n", __LINE__); + { + int serrno = errno; + munmap(xenfb->fb_info, XC_PAGE_SIZE); + errno = serrno; + } + error_kbd: + printf("%d\n", __LINE__); + { + int serrno = errno; + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); + errno = serrno; + } + error_fbdev: + printf("%d\n", __LINE__); + { + int serrno = errno; + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + errno = serrno; + } + error: + printf("%d\n", __LINE__); + if (xsh) { + int serrno = errno; + xs_daemon_close(xsh); + errno = serrno; + } + + return false; +} + +static void xenfb_update(struct xenfb_private *xenfb, int x, int y, int width, int height) +{ + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, x, y, width, height); +} + +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; + 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: + xenfb_update(xenfb, 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; + // FIXME need to notify? +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenkbd_info *info = xenfb->kbd_info; + + prod = info->out_prod; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = info->out_cons; cons != prod; cons++) { + union xenkbd_out_event *event = &XENKBD_OUT_RING_REF(info, cons); + + switch (event->type) { + default: + break; + } + } + mb(); /* ensure we're done with ring contents */ + info->out_cons = cons; + // FIXME need to notify? +} + +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; + + 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; + + 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_button(struct xenfb *xenfb_pub, bool down, int button) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + event.type = XENKBD_TYPE_BUTTON; + event.button.pressed = down ? 1 : 0; + event.button.button = button; + + return xenfb_kbd_event(xenfb, &event); +} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Sat Sep 02 15:19:25 2006 -0400 @@ -0,0 +1,33 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include +#include + +struct xenfb +{ + uint8_t *pixels; + + int row_stride; + int depth; + int width; + int height; + + 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_button(struct xenfb *xenfb, bool down, int button); + +#endif