Files
xen/tools/xenfb/sdlfb.c
T
Steven Smith 14dc449130 [PVFB] Make sure that framebuffer backend goes away when the domain
terminates, and that it cleans up its area of xenstore.

Tidy up argument parsing a little while I'm here.

Signed-off-by: Steven Smith <sos22@cam.ac.uk>
2006-12-01 12:03:15 +00:00

343 lines
7.2 KiB
C

#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' },
{ NULL }
};
int main(int argc, char **argv)
{
struct xenfb *xenfb;
int domid = -1;
char * title = NULL;
fd_set readfds;
int nfds;
struct SDLFBData data;
SDL_Rect r;
struct timeval tv;
SDL_Event event;
int do_quit = 0;
int opt;
char *endp;
int retval;
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;
case '?':
exit(1);
}
}
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) < 0) {
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);
}
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 fds 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);
nfds = xenfb_select_fds(xenfb, &readfds);
tv = (struct timeval){0, 10000};
if (select(nfds, &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;
retval = xenfb_poll(xenfb, &readfds);
if (retval == -2)
xenfb_teardown(xenfb);
if (retval < 0)
break;
}
xenfb_delete(xenfb);
SDL_Quit();
return 0;
}