This is actually adds the shared memory interface to QEMU.
Regards, Anthony Liguori
# HG changeset patch # User [EMAIL PROTECTED] # Node ID 0b4c6f94ee520884063f11f4631185368998cf9c # Parent b9f2ce1d04dc160035f2a8c44fa400912b8a01fd Add shared memory interface for external GUIs. The protocol is documented in README.shmem. diff -r b9f2ce1d04dc -r 0b4c6f94ee52 Makefile.target --- a/Makefile.target Sat Jul 15 03:03:35 2006 +++ b/Makefile.target Sun Jul 16 16:25:28 2006 @@ -375,6 +375,7 @@ VL_OBJS+=sdl.o endif VL_OBJS+=vnc.o +VL_OBJS+=shmem.o ifdef CONFIG_COCOA VL_OBJS+=cocoa.o COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit diff -r b9f2ce1d04dc -r 0b4c6f94ee52 vl.c --- a/vl.c Sat Jul 15 03:03:35 2006 +++ b/vl.c Sun Jul 16 16:25:28 2006 @@ -5221,6 +5221,7 @@ #endif "-loadvm file start right away with a saved state (loadvm in monitor)\n" "-vnc display start a VNC server on display\n" + "-shmem dev start the shared memory interface on char device 'dev'\n" "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -5299,6 +5300,7 @@ QEMU_OPTION_smp, QEMU_OPTION_vnc, QEMU_OPTION_no_acpi, + QEMU_OPTION_shmem, }; typedef struct QEMUOption { @@ -5370,6 +5372,7 @@ { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, { "smp", HAS_ARG, QEMU_OPTION_smp }, { "vnc", HAS_ARG, QEMU_OPTION_vnc }, + { "shmem", HAS_ARG, QEMU_OPTION_shmem }, /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -5604,6 +5607,7 @@ QEMUMachine *machine; char usb_devices[MAX_USB_CMDLINE][128]; int usb_devices_index; + const char *shmem_path = 0; LIST_INIT (&vm_change_state_head); #ifndef _WIN32 @@ -6020,6 +6024,9 @@ case QEMU_OPTION_no_acpi: acpi_enabled = 0; break; + case QEMU_OPTION_shmem: + shmem_path = optarg; + break; } } } @@ -6133,6 +6140,19 @@ dumb_display_init(ds); } else if (vnc_display != -1) { vnc_display_init(ds, vnc_display); + } else if (shmem_path) { + CharDriverState *s; + + s = qemu_chr_open(shmem_path); + if (!s) { + fprintf(stderr, "qemu: could not open shmem device '%s'\n", + shmem_path); + exit(1); + } + if (!strcmp(shmem_path, "vc")) + qemu_chr_printf(s, "shmem console\n"); + + shmem_display_init(ds, s); } else { #if defined(CONFIG_SDL) sdl_display_init(ds, full_screen); diff -r b9f2ce1d04dc -r 0b4c6f94ee52 vl.h --- a/vl.h Sat Jul 15 03:03:35 2006 +++ b/vl.h Sun Jul 16 16:25:28 2006 @@ -767,6 +767,9 @@ /* vnc.c */ void vnc_display_init(DisplayState *ds, int display); +/* shmem.c */ +void shmem_display_init(DisplayState *ds, CharDriverState *s); + /* ide.c */ #define MAX_DISKS 4 diff -r b9f2ce1d04dc -r 0b4c6f94ee52 README.shmem --- /dev/null Sat Jul 15 03:03:35 2006 +++ b/README.shmem Sun Jul 16 16:25:28 2006 @@ -0,0 +1,96 @@ +QEMU Shared Memory Interface + +Changelog + +07-15-2006 Anthony Liguori <[EMAIL PROTECTED]> + - initial draft + +The shared memory interface in qemu provides a way to write external GUIs that +perform almost as well as an integrated GUI. This document attempts to +described the protocol used to interact with this interface. + +Overview + +The transport for the shared memory interface is a QEMU character device. +Currently, QEMU supports a wide variety of devices (TCP/UDP sockets, +ptys, etc). The shared memory interface is enabled with the -shmem option. +The format of the argument for this option is similar to the -serial or +-monitor options. + +The protocol used is asynchronous and line based. Line's are terminated with +a \n (or possibly a \r\n). For the rest of this document, I will refer to +commands sent by QEMU as server commands and commands sent by the other end +of the character device as client commands. + +Server Commands + +ERROR [string] + +This command is sent when an error has occurred (usually from a bad command). +String is a human-readable description of the problem. These errors are +usually not fatal. + +MOUSE-ABSOLUTE + +This command signals to the client that the mouse is now in absolute mode. +This is a hint to the client so that it can handle mouse grab appropriately. + +MOUSE-RELATIVE + +This command signals to the client that the mouse is now in relative mode. +This is a hint to the client so that it can handle mouse grab appropriately. + +RESIZE width, height + +This command signals to the client that the guest has resized the VGA screen. +After a client receives this command, it should allocate an appropriately +sized shared memory segment and notify the server about this with the SHMID +command. + +UPDATE x, y, width, height + +This command signals to the client that the guest has updated a portion of the +VGA screen described by the rectangle (x, y, width, height). + +ATTACHED shmid + +This command signals to the client that the server has attached to the shared +memory segment specified by shmid. The purpose of this command is to allow +the client to know when to mark the shared memory segment as deleted. This +cannot be relied upon in cases where an exit may occur so the client should +take precautions and remove any shared memory segment whenever it is no longer +needed (for instance, after a RESIZE and before the client exits). + +Client Commands + +SHMID shmid, size, width, height, depth, linesize + +This command reports a shared memory segment that the server should use to +render the display too. shmid is a shared memory id (returned from shmget()). +size is the size of the memory (in bytes). width and height represent the +size of the image in pixels. depth is the number of bits per pixel. Finally, +linesize is the number of bytes per line in the image. + +A client should wait for an ATTACHED server command before removing the shared +memory segment. + +MOUSE x, y, buttons + +This command reports a change in mouse state to the server. x and y are the +absolute position of the mouse (relative to the upper left corner of the guests +screen). Both of these values may be negative when in relative mode. buttons +is a bitmask representing which mouse buttons are active. The first bit in the +mask represents the right mouse button, the second represents the left button, +and the third represents the middle mouse button. + +KEY-DOWN key + +This command reports a key press event to the server. key is the name of the +key that has been depressed. The format of key is identical to that used to +represent keys in the keyboard layout files. + +KEY-UP key + +This command reports that a key has been released to the server. key is the +name of the key that has been released. The format of key is identical to that +used to represent keys in the keyboard layout files. diff -r b9f2ce1d04dc -r 0b4c6f94ee52 shmem.c --- /dev/null Sat Jul 15 03:03:35 2006 +++ b/shmem.c Sun Jul 16 16:25:28 2006 @@ -0,0 +1,249 @@ +/* + * QEMU Shared Memory display driver + * + * Copyright (C) 2006 Anthony Liguori <[EMAIL PROTECTED]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" + +#include "shmem_keysym.h" +#include "keymaps.c" + +#include <sys/ipc.h> +#include <sys/shm.h> + +typedef struct _ShmemState +{ + CharDriverState *chr; + DisplayState *ds; + char buffer[1024]; + int n_buffer; + uint8_t *dummy; + int old_x, old_y; + int mouse_mode; /* 0 is relative; 1 is absolute */ + kbd_layout_t *kbd_layout; +} ShmemState; + +static int shmem_can_read(void *opaque) +{ + return 1024; +} + +static void shmem_cmd_shmid(ShmemState *s, int shmid, int size, + int width, int height, int depth, int linesize) +{ + uint8_t *addr; + + addr = shmat(shmid, NULL, 0); + if (addr == (void *)-1) { + qemu_chr_printf(s->chr, "ERROR could not attach to shm:%d\n", shmid); + return; + } + + qemu_chr_printf(s->chr, "ATTACHED %d\n", shmid); + + if (s->ds->data && s->ds->data != s->dummy) + shmdt(s->ds->data); + + s->ds->data = addr; + s->ds->linesize = linesize; + s->ds->depth = depth; + s->ds->width = width; + s->ds->height = height; + + vga_hw_invalidate(); + vga_hw_update(); +} + +static void shmem_update_mouse_type(ShmemState *s) +{ + if (kbd_mouse_is_absolute()) { + if (s->mouse_mode != 1) { + s->mouse_mode = 1; + qemu_chr_printf(s->chr, "MOUSE-ABSOLUTE\n"); + } + } else if (s->mouse_mode != 0) { + s->mouse_mode = 0; + qemu_chr_printf(s->chr, "MOUSE-RELATIVE\n"); + } +} + +static void shmem_cmd_mouse(ShmemState *s, int x, int y, int z, int buttons) +{ + if (s->old_x != -1) { + int dx, dy; + + if (kbd_mouse_is_absolute()) { + dx = x * 0x7FFF / s->ds->width; + dy = y * 0x7FFF / s->ds->height; + } else { + dx = x - s->old_x; + dy = y - s->old_y; + } + + shmem_update_mouse_type(s); + + kbd_mouse_event(dx, dy, z, buttons); + } + + s->old_x = x; + s->old_y = y; +} + +static int keyname2keysym(const char *key) +{ + int i; + + for (i = 0; name2keysym[i].name; i++) { + if (strcmp(name2keysym[i].name, key) == 0) + return name2keysym[i].keysym; + } + + return 0; +} + +static void shmem_cmd_key(ShmemState *s, const char *key, int down) +{ + int keycode; + int keysym; + + keysym = keyname2keysym(key); + if (keysym == 0) { + qemu_chr_printf(s->chr, "ERROR unknown key `%s'\n", key); + return; + } + keycode = keysym2scancode(s->kbd_layout, keysym & 0xFFFF); + + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (down) + kbd_put_keycode(keycode & 0x7F); + else + kbd_put_keycode(keycode | 0x80); +} + +static void shmem_handle_command(ShmemState *s, const char *cmd) +{ + int shmid, size, depth, linesize, width, height; + int x, y, z, buttons; + char key[1024]; + + if (sscanf(cmd, "SHMID %d, %d, %d, %d, %d, %d\n", + &shmid, &size, &width, &height, &depth, &linesize) == 6) { + shmem_cmd_shmid(s, shmid, size, width, height, depth, linesize); + } else if (sscanf(cmd, "MOUSE %d, %d, %d, %d\n", + &x, &y, &z, &buttons) == 4) { + shmem_cmd_mouse(s, x, y, z, buttons); + } else if (sscanf(cmd, "KEY-DOWN %1023s\n", key) == 1) { + shmem_cmd_key(s, key, 1); + } else if (sscanf(cmd, "KEY-UP %1023s\n", key) == 1) { + shmem_cmd_key(s, key, 0); + } else { + qemu_chr_printf(s->chr, "ERROR unknown command: `%s'\n", cmd); + } +} + +static void shmem_read(void *opaque, const uint8_t *buf, int size) +{ + ShmemState *s = opaque; + int offset = 0; + + while (offset < size) { + char *nl; + int len; + + len = MIN(sizeof(s->buffer) - s->n_buffer - 1, size - offset); + memcpy(s->buffer + s->n_buffer, buf + offset, len); + s->n_buffer += len; + s->buffer[s->n_buffer] = 0; + offset += len; + + while ((nl = strchr(s->buffer, '\n'))) { + int remainder = s->n_buffer - (nl - s->buffer) - 1; + *nl = 0; + shmem_handle_command(s, s->buffer); + memmove(s->buffer, nl + 1, remainder); + s->n_buffer = remainder; + } + } +} + +static void shmem_dpy_resize(DisplayState *ds, int w, int h) +{ + ShmemState *s = ds->opaque; + + if (w == ds->width && h == ds->height) + return; + + qemu_chr_printf(s->chr, "RESIZE %d, %d\n", w, h); + + s->dummy = realloc(s->dummy, w * h); + if (s->dummy == NULL) + exit(1); + + ds->depth = 8; + ds->width = w; + ds->height = h; + ds->linesize = w; + ds->data = s->dummy; +} + +static void shmem_dpy_update(DisplayState *ds, int x, int y, int w, int h) +{ + ShmemState *s = ds->opaque; + qemu_chr_printf(s->chr, "UPDATE %d, %d, %d, %d\n", x, y, w, h); +} + +static void shmem_dpy_refresh(DisplayState *ds) +{ + vga_hw_update(); +} + +void shmem_display_init(DisplayState *ds, CharDriverState *chr) +{ + ShmemState *s; + + s = qemu_mallocz(sizeof(ShmemState)); + if (!s) + exit(1); + + s->chr = chr; + s->ds = ds; + s->old_x = -1; + s->old_y = -1; + s->mouse_mode = 0; + + if (!keyboard_layout) + keyboard_layout = "en-us"; + + s->kbd_layout = init_keyboard_layout(keyboard_layout); + if (!s->kbd_layout) + exit(1); + + ds->dpy_update = shmem_dpy_update; + ds->dpy_resize = shmem_dpy_resize; + ds->dpy_refresh = shmem_dpy_refresh; + ds->opaque = s; + + shmem_dpy_resize(ds, 640, 480); + + qemu_chr_add_read_handler(chr, shmem_can_read, shmem_read, s); +} diff -r b9f2ce1d04dc -r 0b4c6f94ee52 shmem_keysym.h --- /dev/null Sat Jul 15 03:03:35 2006 +++ b/shmem_keysym.h Sun Jul 16 16:25:28 2006 @@ -0,0 +1,277 @@ +typedef struct { + const char* name; + int keysym; +} name2keysym_t; +static name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 1}, + { "exclam", 2}, + { "quotedbl", 3}, + { "numbersign", 4}, + { "dollar", 5}, + { "percent", 6}, + { "ampersand", 7}, + { "apostrophe", 8}, + { "parenleft", 9}, + { "parenright", 10}, + { "asterisk", 11}, + { "plus", 12}, + { "comma", 13}, + { "minus", 14}, + { "period", 15}, + { "slash", 16}, + { "0", 17}, + { "1", 18}, + { "2", 19}, + { "3", 20}, + { "4", 21}, + { "5", 22}, + { "6", 23}, + { "7", 24}, + { "8", 25}, + { "9", 26}, + { "colon", 27}, + { "semicolon", 28}, + { "less", 29}, + { "equal", 30}, + { "greater", 31}, + { "question", 32}, + { "at", 33}, + { "A", 34}, + { "B", 35}, + { "C", 36}, + { "D", 37}, + { "E", 38}, + { "F", 39}, + { "G", 40}, + { "H", 41}, + { "I", 42}, + { "J", 43}, + { "K", 44}, + { "L", 45}, + { "M", 46}, + { "N", 47}, + { "O", 48}, + { "P", 49}, + { "Q", 40}, + { "R", 41}, + { "S", 42}, + { "T", 43}, + { "U", 44}, + { "V", 45}, + { "W", 46}, + { "X", 47}, + { "Y", 48}, + { "Z", 49}, + { "bracketleft", 50}, + { "backslash", 51}, + { "bracketright", 52}, + { "asciicircum", 53}, + { "underscore", 54}, + { "grave", 55}, + { "a", 56}, + { "b", 57}, + { "c", 58}, + { "d", 59}, + { "e", 60}, + { "f", 61}, + { "g", 62}, + { "h", 63}, + { "i", 64}, + { "j", 65}, + { "k", 66}, + { "l", 67}, + { "m", 68}, + { "n", 69}, + { "o", 70}, + { "p", 71}, + { "q", 72}, + { "r", 73}, + { "s", 74}, + { "t", 75}, + { "u", 76}, + { "v", 77}, + { "w", 78}, + { "x", 79}, + { "y", 80}, + { "z", 81}, + { "braceleft", 82}, + { "bar", 83}, + { "braceright", 84}, + { "asciitilde", 85}, + +/* latin 1 extensions */ +{ "nobreakspace", 86}, +{ "exclamdown", 87}, +{ "cent", 88}, +{ "sterling", 89}, +{ "currency", 90}, +{ "yen", 91}, +{ "brokenbar", 92}, +{ "section", 93}, +{ "diaeresis", 94}, +{ "copyright", 95}, +{ "ordfeminine", 96}, +{ "guillemotleft", 97}, +{ "notsign", 98}, +{ "hyphen", 99}, +{ "registered", 100}, +{ "macron", 101}, +{ "degree", 102}, +{ "plusminus", 103}, +{ "twosuperior", 104}, +{ "threesuperior", 105}, +{ "acute", 106}, +{ "mu", 107}, +{ "paragraph", 108}, +{ "periodcentered", 109}, +{ "cedilla", 110}, +{ "onesuperior", 111}, +{ "masculine", 112}, +{ "guillemotright", 113}, +{ "onequarter", 114}, +{ "onehalf", 115}, +{ "threequarters", 116}, +{ "questiondown", 117}, +{ "Agrave", 118}, +{ "Aacute", 119}, +{ "Acircumflex", 120}, +{ "Atilde", 121}, +{ "Adiaeresis", 122}, +{ "Aring", 123}, +{ "AE", 124}, +{ "Ccedilla", 125}, +{ "Egrave", 126}, +{ "Eacute", 127}, +{ "Ecircumflex", 128}, +{ "Ediaeresis", 129}, +{ "Igrave", 130}, +{ "Iacute", 131}, +{ "Icircumflex", 132}, +{ "Idiaeresis", 133}, +{ "ETH", 134}, +{ "Eth", 135}, +{ "Ntilde", 136}, +{ "Ograve", 137}, +{ "Oacute", 138}, +{ "Ocircumflex", 139}, +{ "Otilde", 140}, +{ "Odiaeresis", 141}, +{ "multiply", 142}, +{ "Ooblique", 143}, +{ "Oslash", 144}, +{ "Ugrave", 145}, +{ "Uacute", 146}, +{ "Ucircumflex", 147}, +{ "Udiaeresis", 148}, +{ "Yacute", 149}, +{ "THORN", 140}, +{ "Thorn", 141}, +{ "ssharp", 142}, +{ "agrave", 143}, +{ "aacute", 144}, +{ "acircumflex", 145}, +{ "atilde", 146}, +{ "adiaeresis", 147}, +{ "aring", 148}, +{ "ae", 149}, +{ "ccedilla", 150}, +{ "egrave", 151}, +{ "eacute", 152}, +{ "ecircumflex", 153}, +{ "ediaeresis", 154}, +{ "igrave", 155}, +{ "iacute", 156}, +{ "icircumflex", 157}, +{ "idiaeresis", 158}, +{ "eth", 159}, +{ "ntilde", 160}, +{ "ograve", 161}, +{ "oacute", 162}, +{ "ocircumflex", 163}, +{ "otilde", 164}, +{ "odiaeresis", 165}, +{ "division", 166}, +{ "oslash", 167}, +{ "ooblique", 168}, +{ "ugrave", 169}, +{ "uacute", 170}, +{ "ucircumflex", 171}, +{ "udiaeresis", 172}, +{ "yacute", 173}, +{ "thorn", 174}, +{ "ydiaeresis", 175}, +{ "EuroSign", 176}, + + /* modifiers */ +{"Control_L", 177}, +{"Control_R", 178}, +{"Alt_L", 179}, +{"Alt_R", 180}, +{"Caps_Lock", 181}, +{"Meta_L", 182}, +{"Meta_R", 183}, +{"Shift_L", 184}, +{"Shift_R", 185}, +{"Super_L", 186}, +{"Super_R", 187}, + + /* special keys */ +{"BackSpace", 188}, +{"Tab", 189}, +{"Return", 190}, +{"Right", 191}, +{"Left", 192}, +{"Up", 193}, +{"Down", 194}, +{"Page_Down", 195}, +{"Page_Up", 196}, +{"Insert", 197}, +{"Delete", 198}, +{"Home", 199}, +{"End", 200}, +{"Scroll_Lock", 201}, +{"F1", 202}, +{"F2", 203}, +{"F3", 204}, +{"F4", 205}, +{"F5", 206}, +{"F6", 207}, +{"F7", 208}, +{"F8", 209}, +{"F9", 210}, +{"F10", 211}, +{"F11", 212}, +{"F12", 213}, +{"F13", 214}, +{"F14", 215}, +{"F15", 216}, +{"Sys_Req", 217}, +{"KP_0", 218}, +{"KP_1", 219}, +{"KP_2", 220}, +{"KP_3", 221}, +{"KP_4", 222}, +{"KP_5", 223}, +{"KP_6", 224}, +{"KP_7", 225}, +{"KP_8", 226}, +{"KP_9", 227}, +{"KP_Add", 228}, +{"KP_Decimal", 229}, +{"KP_Divide", 230}, +{"KP_Enter", 231}, +{"KP_Equal", 232}, +{"KP_Multiply", 233}, +{"KP_Subtract", 234}, +{"help", 235}, +{"Menu", 236}, +{"Power", 237}, +{"Print", 238}, +{"Mode_switch", 239}, +{"Multi_Key", 240}, +{"Num_Lock", 241}, +{"Pause", 242}, +{"Escape", 243}, +{0}, +};
_______________________________________________ Qemu-devel mailing list Qemu-devel@nongnu.org http://lists.nongnu.org/mailman/listinfo/qemu-devel