[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH] Paravirt framebuffer backend tools [2/5]
Steven Smith <sos22-xen@xxxxxxxxxxxxx> writes: >> --- 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 > You shouldn't need to add -g here; Rules.mk handles it for you if > debug is set. Jeremy took care of this. >> +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 <stdint.h> >> +#include <gdk/gdkkeysyms.h> >> +#include <linux/input.h> >> + >> +uint32_t gdk_linux_mapping[0x10000] = { >> + [GDK_a] = KEY_A, > This is kind of ugly. Is there any chance it could be autogenerated? > Also, where did 0x10000 come from? > > Also, depending on GTK just for the keymap table is a real pain. Or > is it already required for libvncserver? Jeremy answered this one. There was quite a bit of discussion on how to best encode keys (thanks to Laurent for patiently explaining the issue). I'm *not* ignoring that problem. However, the patch is large enough as it is, so let's get the things it addresses nailed down before we address the key code problem. > <snip> > >> + [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 <SDL.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 data > That's a really wonderful name. Jeremy took care of this. >> +{ >> + 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, > Another really ugly mapping table, although not quite as bad as the > GTK one. > > Where'd the magic 1024 come from? Jeremy answered this one. >> + [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; > Slightly strange whitespace, but nevermind. Fixed anyway. >> + >> + while ((opt = getopt_long(argc, argv, "d:t:", options, >> + NULL)) != -1) { >> + switch (opt) { >> + case 'd': >> + domid = strtol(optarg, NULL, 10); > It'd be nice to check for a malformed argument here. Done. >> + break; >> + case 't': >> + title = strdup(optarg); > This can fail. Jeremy answered this one. >> + break; >> + } >> + } >> + if (optind != argc) { >> + fprintf(stderr, "Invalid options!\n"); >> + exit(1); > errx() maybe? Jeremy answered this one. >> + } >> + if (domid == -1) { >> + fprintf(stderr, "Domain ID must be specified!\n"); >> + exit(1); >> + } >> + >> + xenfb = xenfb_new(); >> + if (xenfb == NULL) >> + return 1; > Why have you used exit(1) in some places and return 1 in others? It's all exit(1) now. > Also, an error message here would be a good idea. Done. >> + >> + if (!xenfb_attach_dom(xenfb, domid)) >> + return 1; > An error mesasge would be good. Done. >> + >> + 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"); > This can fail. Jeremy answered this one. >> + 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) { > Select can say -1 because of EINTR (e.g. when strace attaches). It's > not clear to me whether you want to exit or retry in that case. > > Also, if you quit because select returns -1, you need an error > message. Reworked the loop. >> + 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}; > I think 500us is a little short here. About ten milliseconds sounds > more plausible. This is a bit of a bikeshed. Changed to 10ms. > It's a pity SDL doesn't allow you to wait for either an SDL event or > an fd to become readable. Could you do something like spawn a thread > which does the selects in a loop, and then SDL_PushEvent()s a user > event when the fd becomes readable? > > Admittedly, SDL_WaitEvent also contains this kind of loop-with-sleep, > but it'd reduce the number of magic tunables a bit. Added a comment explaining the clunkiness and sketching the solution you proposed. >> + } >> + >> + 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 > > Minor nit: generally, putting a vnc_ prefix on these functions > confused me, since it looks like they should be in libvncserver. This > may just be because I'm not paying enough attention. > >> @@ -0,0 +1,245 @@ >> +#define _GNU_SOURCE >> +#include <errno.h> >> +#include <getopt.h> >> +#include <stdlib.h> >> +#include <unistd.h> >> +#include <malloc.h> >> +#include <rfb/rfb.h> >> +#include <xs.h> >> +#include "xenfb.h" >> + >> +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) >> +{ >> + extern uint32_t gdk_linux_mapping[0x10000]; > Is there any chance of moving this into a header file somewhere? I moved the table into this file instead. >> + 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); > Umm? Anthony's debug code. Gone. >> + 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); > Could fail. Also, consider using asprintf. Jeremy took care of this. >> + if (snprintf(buf, 256, "%s/console/vnc-port", path) == -1) >> + goto out; >> + >> + portstr = malloc(10); > Why is this on the heap rather than the stack? Jeremy took care of this. >> + 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) > I'm not convinced the backend server process is the best place to > start the viewer from. Perhaps xend would be a better choice? Not > sure about this. Jeremy answered this one. >> +{ >> + int pid; >> + char s[16]; >> + >> + snprintf(s, 16, ":%d", port); >> + switch (pid = fork()) { >> + case -1: >> + fprintf(stderr, "vncviewer failed fork\n"); >> + exit(1); > err()? > >> + >> + case 0: /* child */ >> + execlp("vncviewer", "vncviewer", s, 0); >> + fprintf(stderr, "vncviewer execlp failed\n"); >> + exit(1); > err()? > >> + >> + default: >> + return pid; > This is ignored. Also, the parent process makes no attempt to check > whether the child was exec()ed successfully or anything along those > lines. This is enough of a pain to fix that I'd probably just ignore > it, though. Agreed. >> + } >> +} >> + >> +static struct option options[] = { >> + { "domid", 1, NULL, 'd' }, >> + { "vncport", 1, NULL, 'p' }, >> + { "title", 1, NULL, 't' }, >> + { "unused", 0, NULL, 'u' }, > What does this do? Jeremy? >> + { "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; > Whitespace is a bit funny again. Fixed. >> + struct xenfb *xenfb; >> + fd_set readfds; >> + int fd; >> + char buffer[1024]; > Could do with a better name, and is larger than it needs to be. Jeremy took care of this. >> + int opt; >> + bool unused = FALSE; > You're inconsistent about the capitalisation of bools. Jeremy took care of this. >> + 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); > It would be nice to sanity check the argument here. Done. >> + break; >> + case 'p': >> + port = strtol(optarg, NULL, 10); > Again. Done. >> + break; >> + case 't': >> + title = strdup(optarg); > Can fail. > >> + break; >> + case 'u': >> + unused = TRUE; >> + break; >> + case 'l': >> + listen = strdup(optarg); > Can fail. > >> + break; >> + case 'v': >> + viewer = TRUE; >> + break; >> + case 'l': >> + listen = strdup(optarg); > Can fail. > >> + 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; > Umm... What's going on here? Jeremy took care of this. >> + >> + 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 <malloc.h> >> +#include <stdlib.h> >> +#include <sys/types.h> >> +#include <fcntl.h> >> +#include <unistd.h> >> +#include <xenctrl.h> >> +#include <linux/xenfb.h> >> +#include <linux/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 malicous backend? > Well, this is the backend, so defending against a malicious frontend > would be a better choice. Thanks. >> + >> +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; > How do {fbdev,kbd}_port differ from {fbdev,kbd}_evtchn? Jeremy answered this one. > The _evtchn fields are only ever accessed from xenfb_attach_dom. Could > they be locals to that function? Done. >> + 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)); > Use calloc instead of malloc, perhaps? Hate it. >> + >> + 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; > It's a pity we don't have a macro which hides this ugliness. Perhaps > > #define PRESERVING_ERRNO(x) do { > int tmp = errno; > x; > errno = tmp; > } while (0) > > You could then do something like > > if (xenfb_evt_sch == -1) { > PRESERVING_ERRNO(xc_evtchn_close(xenfb->evt_xch);free(xenfb)); > return NULL; > } > > Not sure whether that's more or less ugly, to be honest. I think I prefer my poison straight here, without macro wrapping :) >> + } >> + >> + 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); > Can fail. Jeremy took care of this. >> + 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)); > What happens if this fails? Catched. >> + 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; >> +} > You're somewhat inconsistent about returning error numbers as negative > return values or through errno. I'd prefer the latter in userspace > code, but it doesn't matter too much, privided you pick one. Jeremy took care of this. >> + >> +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) > xenfb_xs_scanf can't return -EAGAIN. What are you trying to achieve > here? Jeremy took care of this. >> + 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"); > Where does this message go? Jeremy answered this one. >> + 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); > Please make fbmfns a local rather than putting it in the info > structure. Done. >> + >> + 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: > The error path here is utterly revolting. Perhaps something like this: > > error: > serrno = errno; > if (xenfb->fb) > munmap(xenfb->fb, xenfb->fb_info->mem_length); > if (fbmfns) > munmap(fbmfns, xenfb->fb_info->mem_length); > ... > errno = serrno; > > return false; > > Or would that be too easy? Done. >> + 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); > I think you may end up closing the connection to the daemon twice here. Don't think so, because xsh is cleared on the other close. >> + 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); >> +} > I'm not convinced this wrapper is actually needed, given that it's > utterly trivial and only called from one place. It's gone. >> + >> +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? > Maybe. If there's any possibility of the frontend queuing evetns when > the ring is full, yes. It doesn't at the moment, but if you want to > add it in the future and maintain forward compatibility you need it. Added the 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? >> +} > > I'd replace this with > > +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) > +{ > + struct xenkbd_info *info = xenfb->kbd_info; > + /* We don't understand any keyboard events, so just ignore them. */ > + info->out_cons = info->out_prod; > +} > > It's smaller, easier to understand, and more efficient. Taken. > As for the FIXME, the protocol spec says you need it, but I'm not sure > it's actually all that useful. It will only make any difference if > the frontend starts queueing keyboard events if it finds the queue to > be full when it tries to send one. Added the 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) > Is this sending XENKBD_TYPE_MOTION or XENFB_TYPE_MOTION? XENFB_TYPE_MOTION doesn't exist anymore. >> +{ >> + 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 <stdbool.h> >> +#include <stdint.h> >> + >> +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 > > Steven. Okay, here's the next patch. It doesn't yet take care of shadow translate mode, just because I want to get it out of the door. Next week, I hope... Oh, and it also doesn't include Daniel Berrange's locking fixes to LibVNCServer, which you really, really need: http://lists.xensource.com/archives/html/xen-devel/2006-09/msg00371.html diff -r 9977b8785570 tools/Makefile --- a/tools/Makefile Fri Sep 29 19:12:15 2006 +0100 +++ b/tools/Makefile Sat Sep 30 09:29:38 2006 +0200 @@ -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)) diff -r 9977b8785570 tools/xenfb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Sat Sep 30 09:29:38 2006 +0200 @@ -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) $(shell pkg-config --cflags gtk+-2.0) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore diff -r 9977b8785570 tools/xenfb/sdlfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Sat Sep 30 09:29:38 2006 +0200 @@ -0,0 +1,216 @@ +#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; +}; + +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); +} + +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 SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp = NULL; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case 'd': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + 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); + } + + 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; + + 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, "Connection to domain broke (%s)\n", + strerror(errno)); + break; + } + + 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 (do_quit) + break; + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff -r 9977b8785570 tools/xenfb/vncfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/vncfb.c Sat Sep 30 09:29:38 2006 +0200 @@ -0,0 +1,402 @@ +#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 <gdk/gdkkeysyms.h> +#include <linux/input.h> +#include <xs.h> +#include "xenfb.h" + +static 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, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + 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))) { + 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[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 int vnc_start_viewer(int port) +{ + int pid; + char s[16]; + + snprintf(s, sizeof(s), ":%d", port); + switch (pid = fork()) { + case -1: + fprintf(stderr, "vncviewer failed fork\n"); + exit(1); + + case 0: /* child */ + execlp("vncviewer", "vncviewer", s, NULL); + 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 portstr[10]; + int opt; + bool unused = false; + bool viewer = false; + char *endp = NULL; + + 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; + case 'v': + viewer = true; + 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); + + if (viewer) + vnc_start_viewer(server->port); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "Connection to domain broke (%s)\n", + strerror(errno)); + break; + } + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff -r 9977b8785570 tools/xenfb/xenfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Sat Sep 30 09:29:38 2006 +0200 @@ -0,0 +1,404 @@ +#include <malloc.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <xenctrl.h> +#include <linux/xenfb.h> +#include <linux/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 malicous frontend? + +struct xenfb_private +{ + struct xenfb pub; + int domid; + 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; +}; + +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; + + if (domp == NULL) + return NULL; + 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)); + if (!p) { + errno = -ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, p, NULL); + if (!p) { + errno = -ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = -EDOM; + return -1; + } + 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; + unsigned long *fbmfns = NULL; + int n_fbmfns, n_fbdirs; + 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_readonly(); + if (!xsh) + goto error; + + p = xenfb_path_in_dom(xsh, domid, "vfb", buffer, sizeof(buffer)); + if (!p) + goto error; + if (!xs_watch(xsh, p, "")) + goto error; + p = xenfb_path_in_dom(xsh, domid, "vkbd", buffer, sizeof(buffer)); + if (!p) + goto error; + if (!xs_watch(xsh, p, "")) + goto error; + + for (;;) { + ret = xenfb_xs_scanf1(xsh, domid, "vfb/0/page-ref", "%lu", + &fbdev_mfn); + if (ret == -1 && errno == -ENOENT) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vfb/0/event-channel", "%u", + &fbdev_evtchn); + if (ret == -1 && errno == -ENOENT) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vkbd/0/page-ref", "%lu", + &kbd_mfn); + if (ret == -1 && errno == -ENOENT) + goto wait; + if (ret < 0) + goto error; + ret = xenfb_xs_scanf1(xsh, domid, "vkbd/0/event-channel", "%u", + &kbd_evtchn); + if (ret == -1 && errno == -ENOENT) + goto wait; + if (ret < 0) + goto error; + break; + + wait: + printf("Waiting for guest framebuffer to connect...\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, + 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; + + 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; + + n_fbmfns = (xenfb->fb_info->mem_length + (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; + + 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; + + event.type = XENFB_TYPE_SET_EVENTS; + event.set_events.flags = XENFB_FLAG_UPDATE; + if (xenfb_fb_event(xenfb, &event)) + goto error; + + munmap(fbmfns, 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: + serrno = errno; + if (xenfb->fb) + munmap(xenfb->fb, xenfb->fb_info->mem_length); + if (fbmfns) + munmap(fbmfns, xenfb->fb_info->mem_length); + 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; + 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_info *info = xenfb->kbd_info; + + /* We don't understand any keyboard events, so just ignore them. */ + 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; + + 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); +} diff -r 9977b8785570 tools/xenfb/xenfb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Sat Sep 30 09:29:38 2006 +0200 @@ -0,0 +1,33 @@ +#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; + + 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 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |