All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.