qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: John Haxby <john.haxby@oracle.com>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH 1 of 3] Fix keymap handling for vnc console
Date: Tue, 16 Dec 2008 15:46:24 +0000	[thread overview]
Message-ID: <4947CD50.8040806@oracle.com> (raw)
In-Reply-To: <4947CA2B.5030401@oracle.com>

Fix keymap handling for international keyboards

Signed-off-by: John Haxby <john.haxby@oracle.com>

 curses_keys.h |    2
 keymaps.c     |  347 ++++++++++++++++++++++++++++++++++++----------------------
 sdl_keysym.h  |    2
 vnc.c         |   87 ++++++++++----
 vnc_keysym.h  |    2
 5 files changed, 286 insertions(+), 154 deletions(-)

diff -up qemu-0.9.1.6042/curses_keys.h.keymap01 qemu-0.9.1.6042/curses_keys.h
--- qemu-0.9.1.6042/curses_keys.h.keymap01	2008-10-28 00:11:06.000000000 +0000
+++ qemu-0.9.1.6042/curses_keys.h	2008-12-16 10:47:36.000000000 +0000
@@ -244,7 +244,7 @@ typedef struct {
 	int keysym;
 } name2keysym_t;
 
-static const name2keysym_t name2keysym[] = {
+static name2keysym_t name2keysym[] = {
     /* Plain ASCII */
     { "space", 0x020 },
     { "exclam", 0x021 },
diff -up qemu-0.9.1.6042/keymaps.c.keymap01 qemu-0.9.1.6042/keymaps.c
--- qemu-0.9.1.6042/keymaps.c.keymap01	2008-10-02 19:26:42.000000000 +0100
+++ qemu-0.9.1.6042/keymaps.c	2008-12-16 10:47:36.000000000 +0000
@@ -22,58 +22,64 @@
  * THE SOFTWARE.
  */
 
+static int cmp_keysym(const void *a, const void *b) {
+    return strcmp(((name2keysym_t *) a)->name, ((name2keysym_t *) b)->name);
+}
+
+
 static int get_keysym(const char *name)
 {
-    const name2keysym_t *p;
-    for(p = name2keysym; p->name != NULL; p++) {
-        if (!strcmp(p->name, name))
-            return p->keysym;
+    static int n = -1;
+    int l, r, m;
+
+    if (n < 0) {
+	for (n = 0; name2keysym[n].name; n++);
+	qsort(name2keysym, n, sizeof(name2keysym_t), cmp_keysym);
+    }
+    l = 0;
+    r = n-1;
+    while (l <= r) {
+	m = (l + r) / 2;
+	int cmp = strcmp(name2keysym[m].name, name);
+	if (cmp < 0)
+	    l = m + 1;
+	else if (cmp > 0)
+	    r = m - 1;
+	else
+	    return name2keysym[m].keysym;
+    }
+    if (name[0] == 'U') {
+	/* unicode symbol key */
+	char *ptr;
+	int k = strtol(name+1, &ptr, 16);
+	return *ptr ? 0 : k;
     }
-    return 0;
 }
 
-struct key_range {
-    int start;
-    int end;
-    struct key_range *next;
-};
+#define MAX_SCANCODE 256
+#define KEY_LOCALSTATE 0x1
+#define KEY_KEYPAD 0x2
 
 #define MAX_NORMAL_KEYCODE 512
 #define MAX_EXTRA_COUNT 256
 typedef struct {
-    uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
-    struct {
-	int keysym;
-	uint16_t keycode;
-    } keysym2keycode_extra[MAX_EXTRA_COUNT];
-    int extra_count;
-    struct key_range *keypad_range;
-    struct key_range *numlock_range;
-} kbd_layout_t;
+    int keysym;
+    int keycode;
+} keysym2keycode_t;
 
-static void add_to_key_range(struct key_range **krp, int code) {
-    struct key_range *kr;
-    for (kr = *krp; kr; kr = kr->next) {
-	if (code >= kr->start && code <= kr->end)
-	    break;
-	if (code == kr->start - 1) {
-	    kr->start--;
-	    break;
-	}
-	if (code == kr->end + 1) {
-	    kr->end++;
-	    break;
-	}
-    }
-    if (kr == NULL) {
-	kr = qemu_mallocz(sizeof(*kr));
-	if (kr) {
-	    kr->start = kr->end = code;
-	    kr->next = *krp;
-	    *krp = kr;
-	}
-    }
-}
+typedef struct {
+    int n;
+    keysym2keycode_t k[MAX_SCANCODE];
+} keysym_map_t;
+
+typedef struct {
+    keysym_map_t plain;
+    keysym_map_t shift;
+    keysym_map_t altgr;
+    keysym_map_t shift_altgr;
+    keysym_map_t numlock;
+    uint32_t flags [MAX_SCANCODE];
+} kbd_layout_t;
 
 static kbd_layout_t *parse_keyboard_layout(const char *language,
 					   kbd_layout_t * k)
@@ -81,105 +87,194 @@ static kbd_layout_t *parse_keyboard_layo
     FILE *f;
     char file_name[1024];
     char line[1024];
-    int len;
 
     snprintf(file_name, sizeof(file_name),
-             "%s/keymaps/%s", bios_dir, language);
+	     "%s/keymaps/%s", bios_dir, language);
 
     if (!k)
 	k = qemu_mallocz(sizeof(kbd_layout_t));
     if (!k)
-        return 0;
+	return 0;
     if (!(f = fopen(file_name, "r"))) {
 	fprintf(stderr,
 		"Could not read keymap file: '%s'\n", file_name);
 	return 0;
     }
-    for(;;) {
-	if (fgets(line, 1024, f) == NULL)
-            break;
-        len = strlen(line);
-        if (len > 0 && line[len - 1] == '\n')
-            line[len - 1] = '\0';
-        if (line[0] == '#')
-	    continue;
-	if (!strncmp(line, "map ", 4))
-	    continue;
-	if (!strncmp(line, "include ", 8)) {
-	    parse_keyboard_layout(line + 8, k);
-        } else {
-	    char *end_of_keysym = line;
-	    while (*end_of_keysym != 0 && *end_of_keysym != ' ')
-		end_of_keysym++;
-	    if (*end_of_keysym) {
-		int keysym;
-		*end_of_keysym = 0;
-		keysym = get_keysym(line);
-		if (keysym == 0) {
-                    //		    fprintf(stderr, "Warning: unknown keysym %s\n", line);
-		} else {
-		    const char *rest = end_of_keysym + 1;
-		    char *rest2;
-		    int keycode = strtol(rest, &rest2, 0);
-
-		    if (rest && strstr(rest, "numlock")) {
-			add_to_key_range(&k->keypad_range, keycode);
-			add_to_key_range(&k->numlock_range, keysym);
-			//fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode);
-		    }
-
-		    /* if(keycode&0x80)
-		       keycode=(keycode<<8)^0x80e0; */
-		    if (keysym < MAX_NORMAL_KEYCODE) {
-			//fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
-			k->keysym2keycode[keysym] = keycode;
-		    } else {
-			if (k->extra_count >= MAX_EXTRA_COUNT) {
-			    fprintf(stderr,
-				    "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n",
-				    line, keysym);
-			} else {
-#if 0
-			    fprintf(stderr, "Setting %d: %d,%d\n",
-				    k->extra_count, keysym, keycode);
-#endif
-			    k->keysym2keycode_extra[k->extra_count].
-				keysym = keysym;
-			    k->keysym2keycode_extra[k->extra_count].
-				keycode = keycode;
-			    k->extra_count++;
-			}
-		    }
-		}
-	    }
+    while (fgets(line, 1024, f)) {
+	char *ptr = strchr(line, '#');
+	char keyname[1024], p1[1024], p2[1024];
+	int keysym, keycode;
+	int shift = 0;
+	int altgr = 0;
+	int addupper = 0;
+	int numlock = 0;
+	int inhibit = 0;
+
+	if (ptr)
+	    *ptr-- = '\0';
+	else
+	    ptr = &line[strlen(line)-1];
+	while (isspace(*ptr))
+	    *ptr-- = '\0';
+	if (!*line)
+	    continue;
+	if (strncmp(line, "map ", 4) == 0)
+	    continue;
+	if (sscanf(line, "include %s", p1) == 1) {
+	    parse_keyboard_layout(p1, k);
+	    continue;
+	}
+	if (sscanf(line, "%s %i %s %s", keyname, &keycode, p1, p2) == 4) {
+	    shift = (strcmp(p1, "shift") == 0 || strcmp(p2, "shift") == 0);
+	    altgr = (strcmp(p1, "altgr") == 0 || strcmp(p2, "altgr") == 0);
+	} else if (sscanf(line, "%s %i %s", keyname, &keycode, p1) == 3) {
+	    shift = (strcmp(p1, "shift") == 0);
+	    altgr = (strcmp(p1, "altgr") == 0);
+	    addupper = (strcmp(p1, "addupper") == 0);
+	    numlock = (strcmp(p1, "numlock") == 0);
+	    inhibit = (strcmp(p1, "inhibit") == 0);
+	} else if (sscanf(line, "%s %i", keyname, &keycode) != 2)
+	    /* silently ignore spurious lines */
+	    continue;
+
+	if (inhibit)
+	    continue;
+	if ((keysym = get_keysym(keyname)) == 0) {
+	    fprintf(stderr, "%s: warning: unknown keysym %s\n",
+		    file_name, keyname);
+	    continue;
+	}
+	if (keycode <= 0 || keycode >= MAX_SCANCODE) {
+	    fprintf(stderr, "%s: warning: keycode %#x for %s out of range\n",
+		    file_name, keycode, keyname);
+	    continue;
+	}
+	if (numlock)
+	    k->numlock.k[keycode].keysym = keysym;
+	else if (shift && altgr)
+	    k->shift_altgr.k[keycode].keysym = keysym;
+	else if (altgr)
+	    k->altgr.k[keycode].keysym = keysym;
+	else if (shift)
+	    k->shift.k[keycode].keysym = keysym;
+	else {
+	    k->plain.k[keycode].keysym = keysym;
+	    if (addupper)
+		k->shift.k[keycode].keysym = keysym + 'A' - 'a';
 	}
     }
     fclose(f);
     return k;
 }
 
+static int cmp_map (const void *a, const void *b) {
+    return ((keysym2keycode_t *) b)->keysym - ((keysym2keycode_t *) a)->keysym;
+}
+
+static void sort_map (keysym_map_t *map) {
+    int i;
+    for (i = 0; i < MAX_SCANCODE; i++)
+	map->k[i].keycode = i;
+    /* sort undefined scancodes to the end */
+    qsort(map->k, MAX_SCANCODE, sizeof(keysym2keycode_t), cmp_map);
+    for (map->n = 0; map->n < MAX_SCANCODE; map->n++)
+	if (!map->k[map->n].keysym)
+	    break;
+}
+
 static void *init_keyboard_layout(const char *language)
 {
-    return parse_keyboard_layout(language, 0);
+    kbd_layout_t *k = parse_keyboard_layout(language, NULL);
+    if (k) {
+	int i;
+	for (i = 0; i < MAX_SCANCODE; i++) {
+	    if (!(k->shift.k[i].keysym
+		  || k->altgr.k[i].keysym
+		  || k->shift_altgr.k[i].keysym))
+		k->flags[i] |= KEY_LOCALSTATE;
+	    if (k->numlock.k[i].keysym)
+		k->flags[i] |= KEY_KEYPAD;
+	}
+	sort_map(&k->plain);
+	sort_map(&k->shift);
+	sort_map(&k->altgr);
+	sort_map(&k->shift_altgr);
+	sort_map(&k->numlock);
+    }
+    return k;
+}
+
+static int keysym2scancode_map (keysym_map_t *map, int keysym)
+{
+    int l = 0, r = map->n - 1, m;
+    while (l <= r) {
+	m = (l + r) / 2;
+	if (map->k[m].keysym == keysym)
+	    return map->k[m].keycode;
+	else if (map->k[m].keysym < keysym)
+	    r = m - 1;
+	else
+	    l = m + 1;
+    }
+    return 0;
 }
 
 static int keysym2scancode(void *kbd_layout, int keysym)
 {
     kbd_layout_t *k = kbd_layout;
-    if (keysym < MAX_NORMAL_KEYCODE) {
-	if (k->keysym2keycode[keysym] == 0)
-	    fprintf(stderr, "Warning: no scancode found for keysym %d\n",
-		    keysym);
-	return k->keysym2keycode[keysym];
-    } else {
-	int i;
-#ifdef XK_ISO_Left_Tab
-	if (keysym == XK_ISO_Left_Tab)
-	    keysym = XK_Tab;
-#endif
-	for (i = 0; i < k->extra_count; i++)
-	    if (k->keysym2keycode_extra[i].keysym == keysym)
-		return k->keysym2keycode_extra[i].keycode;
+    int scancode;
+
+    if ((scancode = keysym2scancode_map(&k->plain, keysym)))
+	return scancode;
+    if ((scancode = keysym2scancode_map(&k->numlock, keysym)))
+	return scancode;
+    if ((scancode = keysym2scancode_map(&k->shift, keysym)))
+	return scancode;
+    if ((scancode = keysym2scancode_map(&k->altgr, keysym)))
+	return scancode;
+    if ((scancode = keysym2scancode_map(&k->shift_altgr, keysym)))
+	return scancode;
+    return 0;
+}
+
+static inline int keysym2scancode1(void *kbd_layout, int keysym,
+				   int *shift, int *altgr)
+{
+    kbd_layout_t *k = kbd_layout;
+    int s;
+
+    /* normal case: keysym can be found the expected modifier's map */
+    if (*shift && *altgr && (s = keysym2scancode_map(&k->shift_altgr, keysym)))
+	return s;
+    if (*altgr && (s = keysym2scancode_map(&k->altgr, keysym)))
+	return s;
+    if (*shift && (s = keysym2scancode_map(&k->shift, keysym)))
+	return s;
+    if ((s = keysym2scancode_map(&k->plain, keysym)))
+	return s;
+    if ((s = keysym2scancode_map(&k->numlock, keysym)))
+	return s;
+
+    /* fallback for when there is some keyboard/state mismatch */
+    if ((s = keysym2scancode_map(&k->plain, keysym))) {
+	*shift = 0;
+	*altgr = 0;
+	return s;
+    }
+    if ((s = keysym2scancode_map(&k->shift, keysym))) {
+	*shift = 1;
+	*altgr = 0;
+	return s;
+    }
+    if ((s = keysym2scancode_map(&k->altgr, keysym))) {
+	*shift = 0;
+	*altgr = 1;
+	return s;
+    }
+    if ((s = keysym2scancode_map(&k->shift_altgr, keysym))) {
+	*shift = 1;
+	*altgr = 1;
+	return s;
     }
     return 0;
 }
@@ -187,21 +282,21 @@ static int keysym2scancode(void *kbd_lay
 static inline int keycode_is_keypad(void *kbd_layout, int keycode)
 {
     kbd_layout_t *k = kbd_layout;
-    struct key_range *kr;
 
-    for (kr = k->keypad_range; kr; kr = kr->next)
-        if (keycode >= kr->start && keycode <= kr->end)
-            return 1;
-    return 0;
+    if (keycode >= 0 && keycode < MAX_SCANCODE)
+	return !!(k->flags[keycode] & KEY_KEYPAD);
 }
 
 static inline int keysym_is_numlock(void *kbd_layout, int keysym)
 {
     kbd_layout_t *k = kbd_layout;
-    struct key_range *kr;
+    return (keysym2scancode_map(&k->numlock, keysym) != 0);
+}
 
-    for (kr = k->numlock_range; kr; kr = kr->next)
-        if (keysym >= kr->start && keysym <= kr->end)
-            return 1;
+static inline int keycode_is_shiftable(void *kbd_layout, int keycode)
+{
+    kbd_layout_t *k = kbd_layout;
+    if (keycode >= 0 || keycode < MAX_SCANCODE)
+	return !(k->flags[keycode] & KEY_LOCALSTATE);
     return 0;
 }
diff -up qemu-0.9.1.6042/sdl_keysym.h.keymap01 qemu-0.9.1.6042/sdl_keysym.h
--- qemu-0.9.1.6042/sdl_keysym.h.keymap01	2008-10-02 19:26:42.000000000 +0100
+++ qemu-0.9.1.6042/sdl_keysym.h	2008-12-16 10:47:36.000000000 +0000
@@ -2,7 +2,7 @@ typedef struct {
 	const char* name;
 	int keysym;
 } name2keysym_t;
-static const name2keysym_t name2keysym[]={
+static name2keysym_t name2keysym[]={
 /* ascii */
     { "space",                0x020},
     { "exclam",               0x021},
diff -up qemu-0.9.1.6042/vnc.c.keymap01 qemu-0.9.1.6042/vnc.c
--- qemu-0.9.1.6042/vnc.c.keymap01	2008-12-13 18:57:12.000000000 +0000
+++ qemu-0.9.1.6042/vnc.c	2008-12-16 10:52:09.000000000 +0000
@@ -1031,14 +1031,22 @@ static void pointer_event(VncState *vs, 
     check_pointer_type_change(vs, kbd_mouse_is_absolute());
 }
 
+static void put_keycode(int keycode, int down)
+{
+    if (keycode & 0x80)
+	kbd_put_keycode(0xe0);
+    if (down)
+	kbd_put_keycode(keycode & 0x7f);
+    else
+	kbd_put_keycode(keycode | 0x80);
+}
+
 static void reset_keys(VncState *vs)
 {
     int i;
     for(i = 0; i < 256; i++) {
         if (vs->modifiers_state[i]) {
-            if (i & 0x80)
-                kbd_put_keycode(0xe0);
-            kbd_put_keycode(i | 0x80);
+            put_keycode(i, 0);
             vs->modifiers_state[i] = 0;
         }
     }
@@ -1046,12 +1054,29 @@ static void reset_keys(VncState *vs)
 
 static void press_key(VncState *vs, int keysym)
 {
-    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
-    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+    put_keycode(keysym2scancode(vs->kbd_layout, keysym), 1);
+    put_keycode(keysym2scancode(vs->kbd_layout, keysym), 0);
+}
+
+static void twiddle_modifiers(VncState *vs, int down, int shift, int altgr)
+{
+    if (shift && !(vs->modifiers_state[0x2a] || vs->modifiers_state[0x36]))
+	put_keycode(0x2a, down);
+    if (!shift && vs->modifiers_state[0x2a])
+	put_keycode(0x2a, !down);
+    if (!shift && vs->modifiers_state[0x36])
+	put_keycode(0x36, !down);
+    if (altgr && !vs->modifiers_state[0xb8])
+	put_keycode(0xb8, down);
+    if (!altgr && vs->modifiers_state[0xb8])
+	put_keycode(0xb8, !down);
 }
 
-static void do_key_event(VncState *vs, int down, int keycode, int sym)
+static void do_key_event(VncState *vs, int down, int keycode, int sym,
+			 int shift, int altgr)
 {
+    int keypad = 0;
+
     /* QEMU console switch */
     switch(keycode) {
     case 0x2a:                          /* Left Shift */
@@ -1059,11 +1084,8 @@ static void do_key_event(VncState *vs, i
     case 0x1d:                          /* Left CTRL */
     case 0x9d:                          /* Right CTRL */
     case 0x38:                          /* Left ALT */
-    case 0xb8:                          /* Right ALT */
-        if (down)
-            vs->modifiers_state[keycode] = 1;
-        else
-            vs->modifiers_state[keycode] = 0;
+    case 0xb8:                          /* Right ALT aka AltGr*/
+	vs->modifiers_state[keycode] = down;
         break;
     case 0x02 ... 0x0a: /* '1' to '9' keys */
         if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
@@ -1074,16 +1096,22 @@ static void do_key_event(VncState *vs, i
         }
         break;
     case 0x3a:			/* CapsLock */
+	if (is_graphic_console())
+	    return;
     case 0x45:			/* NumLock */
         if (!down)
             vs->modifiers_state[keycode] ^= 1;
         break;
     }
 
-    if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+    keypad = keycode_is_keypad(vs->kbd_layout, keycode);
+    if (keypad) {
         /* If the numlock state needs to change then simulate an additional
            keypress before sending this one.  This will happen if the user
-           toggles numlock away from the VNC window.
+           toggles numlock away from the VNC window.  This isn't perfect as
+	   pressig the shift key will typically and temporarily have the
+	   effect of inverting the numlock setting: the shift key will be
+	   effectively cancelled out.
         */
         if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
             if (!vs->modifiers_state[0x45]) {
@@ -1099,12 +1127,14 @@ static void do_key_event(VncState *vs, i
     }
 
     if (is_graphic_console()) {
-        if (keycode & 0x80)
-            kbd_put_keycode(0xe0);
-        if (down)
-            kbd_put_keycode(keycode & 0x7f);
-        else
-            kbd_put_keycode(keycode | 0x80);
+	if (keycode_is_shiftable(vs->kbd_layout, keycode) && !keypad) {
+	    if (down)
+		twiddle_modifiers(vs, down, shift, altgr);
+	    put_keycode(keycode, down);
+	    if (!down)
+		twiddle_modifiers(vs, down, shift, altgr);
+	} else
+	    put_keycode(keycode, down);
     } else {
         /* QEMU console emulation */
         if (down) {
@@ -1153,13 +1183,18 @@ static void do_key_event(VncState *vs, i
 
 static void key_event(VncState *vs, int down, uint32_t sym)
 {
+    int shift;
+    int altgr;
     int keycode;
 
-    if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
-	sym = sym - 'A' + 'a';
-
-    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
-    do_key_event(vs, down, keycode, sym);
+    shift = (vs->modifiers_state[0x2a] || vs->modifiers_state[0x36]);
+    altgr = vs->modifiers_state[0xb8];
+    keycode = keysym2scancode1(vs->kbd_layout, sym & 0xFFFF, &shift, &altgr);
+    if (keycode == 0) {
+	fprintf(stderr, "Key lost : keysym 0x%x(%d)\n", sym, sym);
+	return;
+    }
+    do_key_event(vs, down, keycode, sym, shift, altgr);
 }
 
 static void ext_key_event(VncState *vs, int down,
@@ -1169,7 +1204,9 @@ static void ext_key_event(VncState *vs, 
     if (keyboard_layout)
         key_event(vs, down, sym);
     else
-        do_key_event(vs, down, keycode, sym);
+        do_key_event(vs, down, keycode, sym,
+		     vs->modifiers_state[0x2a] || vs->modifiers_state[0x36],
+		     vs->modifiers_state[0xb8]);
 }
 
 static void framebuffer_update_request(VncState *vs, int incremental,
diff -up qemu-0.9.1.6042/vnc_keysym.h.keymap01 qemu-0.9.1.6042/vnc_keysym.h
--- qemu-0.9.1.6042/vnc_keysym.h.keymap01	2008-10-02 19:26:42.000000000 +0100
+++ qemu-0.9.1.6042/vnc_keysym.h	2008-12-16 10:47:36.000000000 +0000
@@ -2,7 +2,7 @@ typedef struct {
 	const char* name;
 	int keysym;
 } name2keysym_t;
-static const name2keysym_t name2keysym[]={
+static name2keysym_t name2keysym[]={
 /* ascii */
     { "space",                0x020},
     { "exclam",               0x021},

  reply	other threads:[~2008-12-16 15:46 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-12-16 15:32 [Qemu-devel] [PATCH 0 of 3] Fix keymap handling for vnc console John Haxby
2008-12-16 15:46 ` John Haxby [this message]
2009-01-08 20:27   ` [Qemu-devel] [PATCH 1 " Anthony Liguori
2009-01-08 20:46     ` John Haxby
2009-01-08 21:06       ` Anthony Liguori
2008-12-16 15:48 ` [Qemu-devel] [PATCH 2 " John Haxby
2008-12-16 15:49 ` [Qemu-devel] [PATCH 3 " John Haxby
2009-01-07 15:12 ` [Qemu-devel] [PATCH 0 " John Haxby
2009-01-08 20:17   ` Anthony Liguori

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4947CD50.8040806@oracle.com \
    --to=john.haxby@oracle.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).