summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorChanho Park <chanho61.park@samsung.com>2014-06-26 20:28:10 +0900
committerChanho Park <chanho61.park@samsung.com>2014-07-07 16:25:44 +0900
commita15119db2ff5c2fdfdeb913b297bf8aa3399132e (patch)
tree7d6f779408bb772b11c029ab88000fc01856b599 /ui
parent340f06c9eaee097e626c251bf7a013350649c091 (diff)
downloadqemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.gz
qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.tar.bz2
qemu-a15119db2ff5c2fdfdeb913b297bf8aa3399132e.zip
Imported Upstream version 2.0.0upstream/2.0.0
Change-Id: I081766c4314e7893f54fec80b920b1638d15021f
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.objs8
-rw-r--r--ui/cocoa.m182
-rw-r--r--ui/console.c151
-rw-r--r--ui/curses.c91
-rw-r--r--ui/gtk.c228
-rw-r--r--ui/input-legacy.c468
-rw-r--r--ui/input.c697
-rw-r--r--ui/keymaps.c6
-rw-r--r--ui/sdl.c151
-rw-r--r--ui/sdl2-keymap.h266
-rw-r--r--ui/sdl2.c834
-rw-r--r--ui/sdl_keysym.h3
-rw-r--r--ui/spice-core.c100
-rw-r--r--ui/spice-display.c116
-rw-r--r--ui/spice-input.c84
-rw-r--r--ui/vnc-auth-sasl.h1
-rw-r--r--ui/vnc-auth-vencrypt.c2
-rw-r--r--ui/vnc-enc-tight.c2
-rw-r--r--ui/vnc-enc-zywrle.h2
-rw-r--r--ui/vnc-jobs.c8
-rw-r--r--ui/vnc-ws.c1
-rw-r--r--ui/vnc.c300
-rw-r--r--ui/vnc.h11
-rw-r--r--ui/vnc_keysym.h373
24 files changed, 3052 insertions, 1033 deletions
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 6ddc0def6..6f2294efd 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -7,16 +7,14 @@ vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
vnc-obj-y += vnc-jobs.o
-common-obj-y += keymaps.o console.o cursor.o input.o qemu-pixman.o
+common-obj-y += keymaps.o console.o cursor.o input.o input-legacy.o qemu-pixman.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
-common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
+common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
common-obj-$(CONFIG_GTK) += gtk.o x_keymap.o
-$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS)
-
-$(obj)/cocoa.o: $(SRC_PATH)/$(obj)/cocoa.m
+$(obj)/sdl.o $(obj)/sdl_zoom.o $(obj)/sdl2.o: QEMU_CFLAGS += $(SDL_CFLAGS)
$(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS)
diff --git a/ui/cocoa.m b/ui/cocoa.m
index be491794d..f20fd1ffa 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -27,6 +27,7 @@
#include "qemu-common.h"
#include "ui/console.h"
+#include "ui/input.h"
#include "sysemu/sysemu.h"
#ifndef MAC_OS_X_VERSION_10_4
@@ -49,14 +50,6 @@
#endif
#define cgrect(nsrect) (*(CGRect *)&(nsrect))
-#define COCOA_MOUSE_EVENT \
- if (isTabletEnabled) { \
- kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
- } else if (isMouseGrabed) { \
- kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
- } else { \
- [NSApp sendEvent:event]; \
- }
typedef struct {
int width;
@@ -67,6 +60,7 @@ typedef struct {
NSWindow *normalWindow;
static DisplayChangeListener *dcl;
+static int last_buttons;
int gArgc;
char **gArgv;
@@ -129,8 +123,8 @@ int keymap[] =
14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
0, // 52 0x34 Undefined
1, // 53 0x35 0x01 ESC QZ_ESCAPE
- 0, // 54 0x36 QZ_RMETA
- 0, // 55 0x37 QZ_LMETA
+ 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA
+ 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA
42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
56, // 58 0x3A 0x38 L ALT QZ_LALT
@@ -204,10 +198,8 @@ int keymap[] =
200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
-/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
+/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
/*
- 219 // 0xdb e0,5b L GUI
- 220 // 0xdc e0,5c R GUI
221 // 0xdd e0,5d APPS
// E0,2A,E0,37 PRNT SCRN
// E1,1D,45,E1,9D,C5 PAUSE
@@ -240,9 +232,8 @@ int keymap[] =
static int cocoa_keycode_to_qemu(int keycode)
{
- if((sizeof(keymap)/sizeof(int)) <= keycode)
- {
- printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
+ if (ARRAY_SIZE(keymap) <= keycode) {
+ fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode);
return 0;
}
return keymap[keycode];
@@ -262,7 +253,7 @@ static int cocoa_keycode_to_qemu(int keycode)
float cx,cy,cw,ch,cdx,cdy;
CGDataProviderRef dataProviderRef;
int modifiers_state[256];
- BOOL isMouseGrabed;
+ BOOL isMouseGrabbed;
BOOL isFullscreen;
BOOL isAbsoluteEnabled;
BOOL isTabletEnabled;
@@ -273,7 +264,7 @@ static int cocoa_keycode_to_qemu(int keycode)
- (void) toggleFullScreen:(id)sender;
- (void) handleEvent:(NSEvent *)event;
- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
-- (BOOL) isMouseGrabed;
+- (BOOL) isMouseGrabbed;
- (BOOL) isAbsoluteEnabled;
- (float) cdx;
- (float) cdy;
@@ -324,7 +315,12 @@ QemuCocoaView *cocoaView;
CGContextSetShouldAntialias (viewContextRef, NO);
// draw screen bitmap directly to Core Graphics context
- if (dataProviderRef) {
+ if (!dataProviderRef) {
+ // Draw request before any guest device has set up a framebuffer:
+ // just draw an opaque black rectangle
+ CGContextSetRGBFillColor(viewContextRef, 0, 0, 0, 1.0);
+ CGContextFillRect(viewContextRef, NSRectToCGRect(rect));
+ } else {
CGImageRef imageRef = CGImageCreate(
screen.width, //width
screen.height, //height
@@ -408,31 +404,41 @@ QemuCocoaView *cocoaView;
int w = surface_width(surface);
int h = surface_height(surface);
+ bool isResize = (w != screen.width || h != screen.height);
+
+ int oldh = screen.height;
+ if (isResize) {
+ // Resize before we trigger the redraw, or we'll redraw at the wrong size
+ COCOA_DEBUG("switchSurface: new size %d x %d\n", w, h);
+ screen.width = w;
+ screen.height = h;
+ [self setContentDimensions];
+ [self setFrame:NSMakeRect(cx, cy, cw, ch)];
+ }
// update screenBuffer
if (dataProviderRef)
CGDataProviderRelease(dataProviderRef);
//sync host window color space with guests
- screen.bitsPerPixel = surface_bits_per_pixel(surface);
- screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2;
+ screen.bitsPerPixel = surface_bits_per_pixel(surface);
+ screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2;
dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL);
// update windows
if (isFullscreen) {
[[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
- [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:NO animate:NO];
} else {
if (qemu_name)
[normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
- [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + oldh, w, h + [normalWindow frame].size.height - oldh) display:YES animate:NO];
+ }
+
+ if (isResize) {
+ [normalWindow center];
}
- screen.width = w;
- screen.height = h;
- [normalWindow center];
- [self setContentDimensions];
- [self setFrame:NSMakeRect(cx, cy, cw, ch)];
}
- (void) toggleFullScreen:(id)sender
@@ -489,23 +495,28 @@ QemuCocoaView *cocoaView;
int buttons = 0;
int keycode;
+ bool mouse_event = false;
NSPoint p = [event locationInWindow];
switch ([event type]) {
case NSFlagsChanged:
keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) {
+ /* Don't pass command key changes to guest unless mouse is grabbed */
+ keycode = 0;
+ }
+
if (keycode) {
if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
- kbd_put_keycode(keycode);
- kbd_put_keycode(keycode | 0x80);
+ qemu_input_event_send_key_number(dcl->con, keycode, true);
+ qemu_input_event_send_key_number(dcl->con, keycode, false);
} else if (qemu_console_is_graphic(NULL)) {
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
if (modifiers_state[keycode] == 0) { // keydown
- kbd_put_keycode(keycode & 0x7f);
+ qemu_input_event_send_key_number(dcl->con, keycode, true);
modifiers_state[keycode] = 1;
} else { // keyup
- kbd_put_keycode(keycode | 0x80);
+ qemu_input_event_send_key_number(dcl->con, keycode, false);
modifiers_state[keycode] = 0;
}
}
@@ -517,15 +528,15 @@ QemuCocoaView *cocoaView;
}
break;
case NSKeyDown:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
- // forward command Key Combos
- if ([event modifierFlags] & NSCommandKeyMask) {
+ // forward command key combos to the host UI unless the mouse is grabbed
+ if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
[NSApp sendEvent:event];
return;
}
// default
- keycode = cocoa_keycode_to_qemu([event keyCode]);
// handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
@@ -539,9 +550,7 @@ QemuCocoaView *cocoaView;
// handle keys for graphic console
} else if (qemu_console_is_graphic(NULL)) {
- if (keycode & 0x80) //check bit for e0 in front
- kbd_put_keycode(0xe0);
- kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+ qemu_input_event_send_key_number(dcl->con, keycode, true);
// handlekeys for Monitor
} else {
@@ -581,10 +590,15 @@ QemuCocoaView *cocoaView;
break;
case NSKeyUp:
keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ // don't pass the guest a spurious key-up if we treated this
+ // command-key combo as a host UI action
+ if (!isMouseGrabbed && ([event modifierFlags] & NSCommandKeyMask)) {
+ return;
+ }
+
if (qemu_console_is_graphic(NULL)) {
- if (keycode & 0x80)
- kbd_put_keycode(0xe0);
- kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+ qemu_input_event_send_key_number(dcl->con, keycode, false);
}
break;
case NSMouseMoved:
@@ -601,7 +615,7 @@ QemuCocoaView *cocoaView;
}
}
}
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSLeftMouseDown:
if ([event modifierFlags] & NSCommandKeyMask) {
@@ -609,15 +623,15 @@ QemuCocoaView *cocoaView;
} else {
buttons |= MOUSE_EVENT_LBUTTON;
}
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSRightMouseDown:
buttons |= MOUSE_EVENT_RBUTTON;
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSOtherMouseDown:
buttons |= MOUSE_EVENT_MBUTTON;
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSLeftMouseDragged:
if ([event modifierFlags] & NSCommandKeyMask) {
@@ -625,38 +639,40 @@ QemuCocoaView *cocoaView;
} else {
buttons |= MOUSE_EVENT_LBUTTON;
}
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSRightMouseDragged:
buttons |= MOUSE_EVENT_RBUTTON;
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSOtherMouseDragged:
buttons |= MOUSE_EVENT_MBUTTON;
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSLeftMouseUp:
if (isTabletEnabled) {
- COCOA_MOUSE_EVENT
- } else if (!isMouseGrabed) {
+ mouse_event = true;
+ } else if (!isMouseGrabbed) {
if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
[self grabMouse];
} else {
[NSApp sendEvent:event];
}
} else {
- COCOA_MOUSE_EVENT
+ mouse_event = true;
}
break;
case NSRightMouseUp:
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSOtherMouseUp:
- COCOA_MOUSE_EVENT
+ mouse_event = true;
break;
case NSScrollWheel:
- if (isTabletEnabled || isMouseGrabed) {
- kbd_mouse_event(0, 0, -[event deltaY], 0);
+ if (isTabletEnabled || isMouseGrabbed) {
+ buttons |= ([event deltaY] < 0) ?
+ MOUSE_EVENT_WHEELUP : MOUSE_EVENT_WHEELDN;
+ mouse_event = true;
} else {
[NSApp sendEvent:event];
}
@@ -664,6 +680,30 @@ QemuCocoaView *cocoaView;
default:
[NSApp sendEvent:event];
}
+
+ if (mouse_event) {
+ if (last_buttons != buttons) {
+ static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
+ [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
+ [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
+ [INPUT_BUTTON_WHEEL_UP] = MOUSE_EVENT_WHEELUP,
+ [INPUT_BUTTON_WHEEL_DOWN] = MOUSE_EVENT_WHEELDN,
+ };
+ qemu_input_update_buttons(dcl->con, bmap, last_buttons, buttons);
+ last_buttons = buttons;
+ }
+ if (isTabletEnabled) {
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, p.x, screen.width);
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, p.y, screen.height);
+ } else if (isMouseGrabbed) {
+ qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, (int)[event deltaX]);
+ qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);
+ } else {
+ [NSApp sendEvent:event];
+ }
+ qemu_input_event_sync();
+ }
}
- (void) grabMouse
@@ -678,7 +718,7 @@ QemuCocoaView *cocoaView;
}
[NSCursor hide];
CGAssociateMouseAndMouseCursorPosition(FALSE);
- isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
+ isMouseGrabbed = TRUE; // while isMouseGrabbed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
}
- (void) ungrabMouse
@@ -693,11 +733,11 @@ QemuCocoaView *cocoaView;
}
[NSCursor unhide];
CGAssociateMouseAndMouseCursorPosition(TRUE);
- isMouseGrabed = FALSE;
+ isMouseGrabbed = FALSE;
}
- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
-- (BOOL) isMouseGrabed {return isMouseGrabed;}
+- (BOOL) isMouseGrabbed {return isMouseGrabbed;}
- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
- (float) cdx {return cdx;}
- (float) cdy {return cdy;}
@@ -749,7 +789,7 @@ QemuCocoaView *cocoaView;
[normalWindow setContentView:cocoaView];
[normalWindow useOptimizedDrawing:YES];
[normalWindow makeKeyAndOrderFront:self];
- [normalWindow center];
+ [normalWindow center];
}
return self;
@@ -768,14 +808,14 @@ QemuCocoaView *cocoaView;
{
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
- // Display an open dialog box if no argument were passed or
+ // Display an open dialog box if no arguments were passed or
// if qemu was launched from the finder ( the Finder passes "-psn" )
if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
NSOpenPanel *op = [[NSOpenPanel alloc] init];
[op setPrompt:@"Boot image"];
[op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
NSArray *filetypes = [NSArray arrayWithObjects:@"img", @"iso", @"dmg",
- @"qcow", @"cow", @"cloop", @"vmdk", nil];
+ @"qcow", @"qcow2", @"cow", @"cloop", @"vmdk", nil];
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
[op setAllowedFileTypes:filetypes];
[op beginSheetModalForWindow:normalWindow
@@ -823,18 +863,18 @@ QemuCocoaView *cocoaView;
if(returnCode == NSCancelButton) {
exit(0);
} else if(returnCode == NSOKButton) {
- const char *bin = "qemu";
char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding];
- char **argv = (char**)malloc( sizeof(char*)*3 );
+ char **argv = g_new(char *, 4);
[sheet close];
- argv[0] = g_strdup_printf("%s", bin);
- argv[1] = g_strdup_printf("-hda");
- argv[2] = g_strdup_printf("%s", img);
+ argv[0] = g_strdup(gArgv[0]);
+ argv[1] = g_strdup("-hda");
+ argv[2] = g_strdup(img);
+ argv[3] = NULL;
- printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
+ // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img);
[self startEmulationWithArgc:3 argv:(char**)argv];
}
@@ -998,9 +1038,9 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
- if (kbd_mouse_is_absolute()) {
+ if (qemu_input_is_absolute()) {
if (![cocoaView isAbsoluteEnabled]) {
- if ([cocoaView isMouseGrabed]) {
+ if ([cocoaView isMouseGrabbed]) {
[cocoaView ungrabMouse];
}
}
diff --git a/ui/console.c b/ui/console.c
index e3e82979d..e057755c0 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -27,8 +27,8 @@
#include "qemu/timer.h"
#include "qmp-commands.h"
#include "sysemu/char.h"
+#include "trace.h"
-//#define DEBUG_CONSOLE
#define DEFAULT_BACKSCROLL 512
#define MAX_CONSOLES 12
#define CONSOLE_CURSOR_PERIOD 500
@@ -124,6 +124,8 @@ struct QemuConsole {
/* Graphic console state. */
Object *device;
+ uint32_t head;
+ QemuUIInfo ui_info;
const GraphicHwOps *hw_ops;
void *hw;
@@ -161,7 +163,7 @@ struct QemuConsole {
};
struct DisplayState {
- struct QEMUTimer *gui_timer;
+ QEMUTimer *gui_timer;
uint64_t last_update;
uint64_t update_interval;
bool refreshing;
@@ -208,8 +210,8 @@ static void gui_update(void *opaque)
}
trace_console_refresh(interval);
}
- ds->last_update = qemu_get_clock_ms(rt_clock);
- qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
+ ds->last_update = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ timer_mod(ds->gui_timer, ds->last_update + interval);
}
static void gui_setup_refresh(DisplayState *ds)
@@ -232,12 +234,12 @@ static void gui_setup_refresh(DisplayState *ds)
}
if (need_timer && ds->gui_timer == NULL) {
- ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
- qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
+ ds->gui_timer = timer_new_ms(QEMU_CLOCK_REALTIME, gui_update, ds);
+ timer_mod(ds->gui_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
}
if (!need_timer && ds->gui_timer != NULL) {
- qemu_del_timer(ds->gui_timer);
- qemu_free_timer(ds->gui_timer);
+ timer_del(ds->gui_timer);
+ timer_free(ds->gui_timer);
ds->gui_timer = NULL;
}
@@ -409,39 +411,6 @@ static const pixman_color_t color_table_rgb[2][8] = {
}
};
-#ifdef DEBUG_CONSOLE
-static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
-{
- if (t_attrib->bold) {
- printf("b");
- } else {
- printf(" ");
- }
- if (t_attrib->uline) {
- printf("u");
- } else {
- printf(" ");
- }
- if (t_attrib->blink) {
- printf("l");
- } else {
- printf(" ");
- }
- if (t_attrib->invers) {
- printf("i");
- } else {
- printf(" ");
- }
- if (t_attrib->unvisible) {
- printf("n");
- } else {
- printf(" ");
- }
-
- printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
-}
-#endif
-
static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
TextAttributes *t_attrib)
{
@@ -899,10 +868,8 @@ static void console_putchar(QemuConsole *s, int ch)
s->nb_esc_params++;
if (ch == ';')
break;
-#ifdef DEBUG_CONSOLE
- fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
- s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
-#endif
+ trace_console_putchar_csi(s->esc_params[0], s->esc_params[1],
+ ch, s->nb_esc_params);
s->state = TTY_STATE_NORM;
switch(ch) {
case 'A':
@@ -1016,9 +983,7 @@ static void console_putchar(QemuConsole *s, int ch)
s->y = s->y_saved;
break;
default:
-#ifdef DEBUG_CONSOLE
- fprintf(stderr, "unhandled escape character '%c'\n", ch);
-#endif
+ trace_console_putchar_unhandled(ch);
break;
}
break;
@@ -1040,7 +1005,7 @@ void console_select(unsigned int index)
DisplayState *ds = s->ds;
if (active_console && active_console->cursor_timer) {
- qemu_del_timer(active_console->cursor_timer);
+ timer_del(active_console->cursor_timer);
}
active_console = s;
if (ds->have_gfx) {
@@ -1059,8 +1024,8 @@ void console_select(unsigned int index)
dpy_text_resize(s, s->width, s->height);
}
if (s->cursor_timer) {
- qemu_mod_timer(s->cursor_timer,
- qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
+ timer_mod(s->cursor_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
}
}
}
@@ -1105,7 +1070,7 @@ static void kbd_send_chars(void *opaque)
/* characters are pending: we send them a bit later (XXX:
horrible, should change char device API) */
if (s->out_fifo.count > 0) {
- qemu_mod_timer(s->kbd_timer, qemu_get_clock_ms(rt_clock) + 1);
+ timer_mod(s->kbd_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
}
}
@@ -1215,7 +1180,12 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type)
obj = object_new(TYPE_QEMU_CONSOLE);
s = QEMU_CONSOLE(obj);
object_property_add_link(obj, "device", TYPE_DEVICE,
- (Object **)&s->device, &local_err);
+ (Object **)&s->device,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &local_err);
+ object_property_add_uint32_ptr(obj, "head",
+ &s->head, &local_err);
if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
(console_type == GRAPHIC_CONSOLE))) {
@@ -1366,7 +1336,7 @@ void update_displaychangelistener(DisplayChangeListener *dcl,
dcl->update_interval = interval;
if (!ds->refreshing && ds->update_interval > interval) {
- qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
+ timer_mod(ds->gui_timer, ds->last_update + interval);
}
}
@@ -1381,6 +1351,16 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
gui_setup_refresh(ds);
}
+int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info)
+{
+ assert(con != NULL);
+ con->ui_info = *info;
+ if (con->hw_ops->ui_info) {
+ return con->hw_ops->ui_info(con->hw, con->head, info);
+ }
+ return -1;
+}
+
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
{
DisplayState *s = con->ds;
@@ -1606,7 +1586,7 @@ DisplayState *init_displaystate(void)
return display_state;
}
-QemuConsole *graphic_console_init(DeviceState *dev,
+QemuConsole *graphic_console_init(DeviceState *dev, uint32_t head,
const GraphicHwOps *hw_ops,
void *opaque)
{
@@ -1624,6 +1604,8 @@ QemuConsole *graphic_console_init(DeviceState *dev,
if (dev) {
object_property_set_link(OBJECT(s), OBJECT(dev),
"device", &local_err);
+ object_property_set_int(OBJECT(s), head,
+ "head", &local_err);
}
s->surface = qemu_create_displaysurface(width, height);
@@ -1638,10 +1620,11 @@ QemuConsole *qemu_console_lookup_by_index(unsigned int index)
return consoles[index];
}
-QemuConsole *qemu_console_lookup_by_device(DeviceState *dev)
+QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
{
Error *local_err = NULL;
Object *obj;
+ uint32_t h;
int i;
for (i = 0; i < nb_consoles; i++) {
@@ -1650,9 +1633,15 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev)
}
obj = object_property_get_link(OBJECT(consoles[i]),
"device", &local_err);
- if (DEVICE(obj) == dev) {
- return consoles[i];
+ if (DEVICE(obj) != dev) {
+ continue;
}
+ h = object_property_get_int(OBJECT(consoles[i]),
+ "head", &local_err);
+ if (h != head) {
+ continue;
+ }
+ return consoles[i];
}
return NULL;
}
@@ -1678,6 +1667,44 @@ bool qemu_console_is_fixedsize(QemuConsole *con)
return con && (con->console_type != TEXT_CONSOLE);
}
+int qemu_console_get_index(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? con->index : -1;
+}
+
+uint32_t qemu_console_get_head(QemuConsole *con)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? con->head : -1;
+}
+
+QemuUIInfo *qemu_console_get_ui_info(QemuConsole *con)
+{
+ assert(con != NULL);
+ return &con->ui_info;
+}
+
+int qemu_console_get_width(QemuConsole *con, int fallback)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? surface_width(con->surface) : fallback;
+}
+
+int qemu_console_get_height(QemuConsole *con, int fallback)
+{
+ if (con == NULL) {
+ con = active_console;
+ }
+ return con ? surface_height(con->surface) : fallback;
+}
+
static void text_console_set_echo(CharDriverState *chr, bool echo)
{
QemuConsole *s = chr->opaque;
@@ -1691,8 +1718,8 @@ static void text_console_update_cursor(void *opaque)
s->cursor_visible_phase = !s->cursor_visible_phase;
graphic_hw_invalidate(s);
- qemu_mod_timer(s->cursor_timer,
- qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
+ timer_mod(s->cursor_timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + CONSOLE_CURSOR_PERIOD / 2);
}
static const GraphicHwOps text_console_ops = {
@@ -1712,7 +1739,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
s->out_fifo.buf = s->out_fifo_buf;
s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
- s->kbd_timer = qemu_new_timer_ms(rt_clock, kbd_send_chars, s);
+ s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s);
s->ds = ds;
s->y_displayed = 0;
@@ -1729,7 +1756,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
}
s->cursor_timer =
- qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
+ timer_new_ms(QEMU_CLOCK_REALTIME, text_console_update_cursor, s);
s->hw_ops = &text_console_ops;
s->hw = s;
diff --git a/ui/curses.c b/ui/curses.c
index 289a9558d..b044790e4 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -30,6 +30,7 @@
#include "qemu-common.h"
#include "ui/console.h"
+#include "ui/input.h"
#include "sysemu/sysemu.h"
#define FONT_HEIGHT 16
@@ -106,9 +107,9 @@ static void curses_resize(DisplayChangeListener *dcl,
curses_calc_pad();
}
-#ifndef _WIN32
-#if defined(SIGWINCH) && defined(KEY_RESIZE)
-static void curses_winch_handler(int signum)
+#if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
+static volatile sig_atomic_t got_sigwinch;
+static void curses_winch_check(void)
{
struct winsize {
unsigned short ws_row;
@@ -117,18 +118,34 @@ static void curses_winch_handler(int signum)
unsigned short ws_ypixel; /* unused */
} ws;
- /* terminal size changed */
- if (ioctl(1, TIOCGWINSZ, &ws) == -1)
+ if (!got_sigwinch) {
return;
+ }
+ got_sigwinch = false;
+
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
+ return;
+ }
resize_term(ws.ws_row, ws.ws_col);
- curses_calc_pad();
invalidate = 1;
+}
- /* some systems require this */
- signal(SIGWINCH, curses_winch_handler);
+static void curses_winch_handler(int signum)
+{
+ got_sigwinch = true;
}
-#endif
+
+static void curses_winch_init(void)
+{
+ struct sigaction old, winch = {
+ .sa_handler = curses_winch_handler,
+ };
+ sigaction(SIGWINCH, &winch, &old);
+}
+#else
+static void curses_winch_check(void) {}
+static void curses_winch_init(void) {}
#endif
static void curses_cursor_position(DisplayChangeListener *dcl,
@@ -163,6 +180,8 @@ static void curses_refresh(DisplayChangeListener *dcl)
{
int chr, nextchr, keysym, keycode, keycode_alt;
+ curses_winch_check();
+
if (invalidate) {
clear();
refresh();
@@ -256,32 +275,34 @@ static void curses_refresh(DisplayChangeListener *dcl)
if (qemu_console_is_graphic(NULL)) {
/* since terminals don't know about key press and release
* events, we need to emit both for each key received */
- if (keycode & SHIFT)
- kbd_put_keycode(SHIFT_CODE);
- if (keycode & CNTRL)
- kbd_put_keycode(CNTRL_CODE);
- if (keycode & ALT)
- kbd_put_keycode(ALT_CODE);
+ if (keycode & SHIFT) {
+ qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
+ }
+ if (keycode & CNTRL) {
+ qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
+ }
+ if (keycode & ALT) {
+ qemu_input_event_send_key_number(NULL, ALT_CODE, true);
+ }
if (keycode & ALTGR) {
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(ALT_CODE);
+ qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
}
- if (keycode & GREY)
- kbd_put_keycode(GREY_CODE);
- kbd_put_keycode(keycode & KEY_MASK);
- if (keycode & GREY)
- kbd_put_keycode(GREY_CODE);
- kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+
+ qemu_input_event_send_key_number(NULL, keycode, true);
+ qemu_input_event_send_key_number(NULL, keycode, false);
+
if (keycode & ALTGR) {
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+ qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
+ }
+ if (keycode & ALT) {
+ qemu_input_event_send_key_number(NULL, ALT_CODE, false);
+ }
+ if (keycode & CNTRL) {
+ qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
+ }
+ if (keycode & SHIFT) {
+ qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
}
- if (keycode & ALT)
- kbd_put_keycode(ALT_CODE | KEY_RELEASE);
- if (keycode & CNTRL)
- kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
- if (keycode & SHIFT)
- kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
} else {
keysym = curses2qemu[chr];
if (keysym == -1)
@@ -349,13 +370,7 @@ void curses_display_init(DisplayState *ds, int full_screen)
curses_keyboard_setup();
atexit(curses_atexit);
-#ifndef _WIN32
-#if defined(SIGWINCH) && defined(KEY_RESIZE)
- /* some curses implementations provide a handler, but we
- * want to be sure this is handled regardless of the library */
- signal(SIGWINCH, curses_winch_handler);
-#endif
-#endif
+ curses_winch_init();
dcl = (DisplayChangeListener *) g_malloc0(sizeof(DisplayChangeListener));
dcl->ops = &dcl_ops;
diff --git a/ui/gtk.c b/ui/gtk.c
index c38146f80..00fbbccb3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -34,6 +34,10 @@
#define GETTEXT_PACKAGE "qemu"
#define LOCALEDIR "po"
+#ifdef _WIN32
+# define _WIN32_WINNT 0x0601 /* needed to get definition of MAPVK_VK_TO_VSC */
+#endif
+
#include "qemu-common.h"
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
@@ -50,30 +54,25 @@
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <locale.h>
+#if defined(CONFIG_VTE)
#include <vte/vte.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/wait.h>
+#endif
#include <math.h>
+#include "trace.h"
#include "ui/console.h"
+#include "ui/input.h"
#include "sysemu/sysemu.h"
#include "qmp-commands.h"
#include "x_keymap.h"
#include "keymaps.h"
#include "sysemu/char.h"
-//#define DEBUG_GTK
-
-#ifdef DEBUG_GTK
-#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
#define MAX_VCS 10
+#if !defined(CONFIG_VTE)
+# define VTE_CHECK_VERSION(a, b, c) 0
+#endif
/* Compatibility define to let us build on both Gtk2 and Gtk3 */
#if GTK_CHECK_VERSION(3, 0, 0)
@@ -111,8 +110,10 @@ typedef struct VirtualConsole
{
GtkWidget *menu_item;
GtkWidget *terminal;
+#if defined(CONFIG_VTE)
GtkWidget *scrolled_window;
CharDriverState *chr;
+#endif
int fd;
} VirtualConsole;
@@ -155,8 +156,11 @@ typedef struct GtkDisplayState
DisplayChangeListener dcl;
DisplaySurface *ds;
int button_mask;
+ gboolean last_set;
int last_x;
int last_y;
+ int grab_x_root;
+ int grab_y_root;
double scale_x;
double scale_y;
@@ -200,7 +204,7 @@ static void gd_update_cursor(GtkDisplayState *s, gboolean override)
on_vga = gd_on_vga(s);
if ((override || on_vga) &&
- (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
+ (s->full_screen || qemu_input_is_absolute() || gd_is_grab_active(s))) {
gdk_window_set_cursor(window, s->null_cursor);
} else {
gdk_window_set_cursor(window, NULL);
@@ -287,10 +291,7 @@ static void gtk_release_modifiers(GtkDisplayState *s)
if (!s->modifier_pressed[i]) {
continue;
}
- if (keycode & SCANCODE_GREY) {
- kbd_put_keycode(SCANCODE_EMUL0);
- }
- kbd_put_keycode(keycode | SCANCODE_UP);
+ qemu_input_event_send_key_number(s->dcl.con, keycode, false);
s->modifier_pressed[i] = false;
}
}
@@ -306,7 +307,7 @@ static void gd_update(DisplayChangeListener *dcl,
int fbw, fbh;
int ww, wh;
- DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
+ trace_gd_update(x, y, w, h);
if (s->convert) {
pixman_image_composite(PIXMAN_OP_SRC, s->ds->image, NULL, s->convert,
@@ -349,13 +350,17 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
GdkDeviceManager *mgr;
gint x_root, y_root;
+ if (qemu_input_is_absolute()) {
+ return;
+ }
+
dpy = gtk_widget_get_display(s->drawing_area);
mgr = gdk_display_get_device_manager(dpy);
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
x, y, &x_root, &y_root);
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
gtk_widget_get_screen(s->drawing_area),
- x, y);
+ x_root, y_root);
}
#else
static void gd_mouse_set(DisplayChangeListener *dcl,
@@ -364,6 +369,10 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
gint x_root, y_root;
+ if (qemu_input_is_absolute()) {
+ return;
+ }
+
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
x, y, &x_root, &y_root);
gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),
@@ -400,8 +409,7 @@ static void gd_switch(DisplayChangeListener *dcl,
GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
bool resized = true;
- DPRINTF("resize(width=%d, height=%d)\n",
- surface_width(surface), surface_height(surface));
+ trace_gd_switch(surface_width(surface), surface_height(surface));
if (s->surface) {
cairo_surface_destroy(s->surface);
@@ -468,8 +476,15 @@ static void gd_change_runstate(void *opaque, int running, RunState state)
static void gd_mouse_mode_change(Notifier *notify, void *data)
{
- gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
- FALSE);
+ GtkDisplayState *s;
+
+ s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
+ /* release the grab at switching to absolute mode */
+ if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ FALSE);
+ }
+ gd_update_cursor(s, FALSE);
}
/** GTK Events **/
@@ -590,7 +605,6 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
void *opaque)
{
GtkDisplayState *s = opaque;
- int dx, dy;
int x, y;
int mx, my;
int fbh, fbw;
@@ -612,31 +626,27 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
x = (motion->x - mx) / s->scale_x;
y = (motion->y - my) / s->scale_y;
- if (x < 0 || y < 0 ||
- x >= surface_width(s->ds) ||
- y >= surface_height(s->ds)) {
- return TRUE;
- }
-
- if (kbd_mouse_is_absolute()) {
- dx = x * 0x7FFF / (surface_width(s->ds) - 1);
- dy = y * 0x7FFF / (surface_height(s->ds) - 1);
- } else if (s->last_x == -1 || s->last_y == -1) {
- dx = 0;
- dy = 0;
- } else {
- dx = x - s->last_x;
- dy = y - s->last_y;
+ if (qemu_input_is_absolute()) {
+ if (x < 0 || y < 0 ||
+ x >= surface_width(s->ds) ||
+ y >= surface_height(s->ds)) {
+ return TRUE;
+ }
+ qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_X, x,
+ surface_width(s->ds));
+ qemu_input_queue_abs(s->dcl.con, INPUT_AXIS_Y, y,
+ surface_height(s->ds));
+ qemu_input_event_sync();
+ } else if (s->last_set && gd_is_grab_active(s)) {
+ qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_X, x - s->last_x);
+ qemu_input_queue_rel(s->dcl.con, INPUT_AXIS_Y, y - s->last_y);
+ qemu_input_event_sync();
}
-
s->last_x = x;
s->last_y = y;
+ s->last_set = TRUE;
- if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
- kbd_mouse_event(dx, dy, 0, s->button_mask);
- }
-
- if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
+ if (!qemu_input_is_absolute() && gd_is_grab_active(s)) {
GdkScreen *screen = gtk_widget_get_screen(s->drawing_area);
int x = (int)motion->x_root;
int y = (int)motion->y_root;
@@ -669,8 +679,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
GdkDisplay *display = gtk_widget_get_display(widget);
gdk_display_warp_pointer(display, screen, x, y);
#endif
- s->last_x = -1;
- s->last_y = -1;
+ s->last_set = FALSE;
return FALSE;
}
}
@@ -681,46 +690,67 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
void *opaque)
{
GtkDisplayState *s = opaque;
- int dx, dy;
- int n;
+ InputButton btn;
+
+ /* implicitly grab the input at the first click in the relative mode */
+ if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
+ !qemu_input_is_absolute() && !gd_is_grab_active(s)) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ TRUE);
+ return TRUE;
+ }
if (button->button == 1) {
- n = 0x01;
+ btn = INPUT_BUTTON_LEFT;
} else if (button->button == 2) {
- n = 0x04;
+ btn = INPUT_BUTTON_MIDDLE;
} else if (button->button == 3) {
- n = 0x02;
+ btn = INPUT_BUTTON_RIGHT;
} else {
- n = 0x00;
+ return TRUE;
}
- if (button->type == GDK_BUTTON_PRESS) {
- s->button_mask |= n;
- } else if (button->type == GDK_BUTTON_RELEASE) {
- s->button_mask &= ~n;
- }
+ qemu_input_queue_btn(s->dcl.con, btn, button->type == GDK_BUTTON_PRESS);
+ qemu_input_event_sync();
+ return TRUE;
+}
- if (kbd_mouse_is_absolute()) {
- dx = s->last_x * 0x7FFF / (surface_width(s->ds) - 1);
- dy = s->last_y * 0x7FFF / (surface_height(s->ds) - 1);
+static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
+ void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ InputButton btn;
+
+ if (scroll->direction == GDK_SCROLL_UP) {
+ btn = INPUT_BUTTON_WHEEL_UP;
+ } else if (scroll->direction == GDK_SCROLL_DOWN) {
+ btn = INPUT_BUTTON_WHEEL_DOWN;
} else {
- dx = 0;
- dy = 0;
+ return TRUE;
}
- kbd_mouse_event(dx, dy, 0, s->button_mask);
-
+ qemu_input_queue_btn(s->dcl.con, btn, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(s->dcl.con, btn, false);
+ qemu_input_event_sync();
return TRUE;
}
static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
{
GtkDisplayState *s = opaque;
- int gdk_keycode;
- int qemu_keycode;
+ int gdk_keycode = key->hardware_keycode;
int i;
- gdk_keycode = key->hardware_keycode;
+#ifdef _WIN32
+ UINT qemu_keycode = MapVirtualKey(gdk_keycode, MAPVK_VK_TO_VSC);
+ switch (qemu_keycode) {
+ case 103: /* alt gr */
+ qemu_keycode = 56 | SCANCODE_GREY;
+ break;
+ }
+#else
+ int qemu_keycode;
if (gdk_keycode < 9) {
qemu_keycode = 0;
@@ -735,10 +765,10 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
} else {
qemu_keycode = 0;
}
+#endif
- DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
- gdk_keycode, qemu_keycode,
- (key->type == GDK_KEY_PRESS) ? "down" : "up");
+ trace_gd_key_event(gdk_keycode, qemu_keycode,
+ (key->type == GDK_KEY_PRESS) ? "down" : "up");
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
if (qemu_keycode == modifier_keycode[i]) {
@@ -746,21 +776,20 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
}
}
- if (qemu_keycode & SCANCODE_GREY) {
- kbd_put_keycode(SCANCODE_EMUL0);
- }
-
- if (key->type == GDK_KEY_PRESS) {
- kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
- } else if (key->type == GDK_KEY_RELEASE) {
- kbd_put_keycode(qemu_keycode | SCANCODE_UP);
- } else {
- g_assert_not_reached();
- }
+ qemu_input_event_send_key_number(s->dcl.con, qemu_keycode,
+ key->type == GDK_KEY_PRESS);
return TRUE;
}
+static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
+{
+ if (event->type == GDK_MOTION_NOTIFY) {
+ return gd_motion_event(widget, &event->motion, opaque);
+ }
+ return FALSE;
+}
+
/** Window Menu Actions **/
static void gd_menu_pause(GtkMenuItem *item, void *opaque)
@@ -959,8 +988,8 @@ static void gd_ungrab_keyboard(GtkDisplayState *s)
static void gd_grab_pointer(GtkDisplayState *s)
{
-#if GTK_CHECK_VERSION(3, 0, 0)
GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+#if GTK_CHECK_VERSION(3, 0, 0)
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
GDK_DEVICE_TYPE_MASTER);
@@ -984,6 +1013,8 @@ static void gd_grab_pointer(GtkDisplayState *s)
tmp = tmp->next;
}
g_list_free(devices);
+ gdk_device_get_position(gdk_device_manager_get_client_pointer(mgr),
+ NULL, &s->grab_x_root, &s->grab_y_root);
#else
gdk_pointer_grab(gtk_widget_get_window(s->drawing_area),
FALSE, /* All events to come to our window directly */
@@ -995,13 +1026,15 @@ static void gd_grab_pointer(GtkDisplayState *s)
NULL, /* Allow cursor to move over entire desktop */
s->null_cursor,
GDK_CURRENT_TIME);
+ gdk_display_get_pointer(display, NULL,
+ &s->grab_x_root, &s->grab_y_root, NULL);
#endif
}
static void gd_ungrab_pointer(GtkDisplayState *s)
{
-#if GTK_CHECK_VERSION(3, 0, 0)
GdkDisplay *display = gtk_widget_get_display(s->drawing_area);
+#if GTK_CHECK_VERSION(3, 0, 0)
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
GList *devices = gdk_device_manager_list_devices(mgr,
GDK_DEVICE_TYPE_MASTER);
@@ -1015,8 +1048,14 @@ static void gd_ungrab_pointer(GtkDisplayState *s)
tmp = tmp->next;
}
g_list_free(devices);
+ gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
+ gtk_widget_get_screen(s->drawing_area),
+ s->grab_x_root, s->grab_y_root);
#else
gdk_pointer_ungrab(GDK_CURRENT_TIME);
+ gdk_display_warp_pointer(display,
+ gtk_widget_get_screen(s->drawing_area),
+ s->grab_x_root, s->grab_y_root);
#endif
}
@@ -1066,6 +1105,7 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
if (arg2 == 0) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
} else {
+#if defined(CONFIG_VTE)
VirtualConsole *vc = &s->vc[arg2 - 1];
VteTerminal *term = VTE_TERMINAL(vc->terminal);
int width, height;
@@ -1075,6 +1115,9 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
gtk_widget_set_size_request(vc->terminal, width, height);
+#else
+ g_assert_not_reached();
+#endif
}
gtk_widget_set_sensitive(s->grab_item, on_vga);
@@ -1120,7 +1163,7 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
VirtualConsole *vc = chr->opaque;
- return write(vc->fd, buf, len);
+ return vc ? write(vc->fd, buf, len) : len;
}
static int nb_vcs;
@@ -1145,6 +1188,7 @@ void early_gtk_display_init(void)
register_vc_handler(gd_vc_handler);
}
+#if defined(CONFIG_VTE)
static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
{
VirtualConsole *vc = opaque;
@@ -1160,10 +1204,12 @@ static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
return TRUE;
}
+#endif
static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group,
GtkWidget *view_menu)
{
+#if defined(CONFIG_VTE)
const char *label;
char buffer[32];
char path[32];
@@ -1233,6 +1279,7 @@ static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSL
chan = g_io_channel_unix_new(vc->fd);
g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
+#endif /* CONFIG_VTE */
return group;
}
@@ -1255,12 +1302,14 @@ static void gd_connect_signals(GtkDisplayState *s)
g_signal_connect(s->drawing_area, "expose-event",
G_CALLBACK(gd_expose_event), s);
#endif
- g_signal_connect(s->drawing_area, "motion-notify-event",
- G_CALLBACK(gd_motion_event), s);
+ g_signal_connect(s->drawing_area, "event",
+ G_CALLBACK(gd_event), s);
g_signal_connect(s->drawing_area, "button-press-event",
G_CALLBACK(gd_button_event), s);
g_signal_connect(s->drawing_area, "button-release-event",
G_CALLBACK(gd_button_event), s);
+ g_signal_connect(s->drawing_area, "scroll-event",
+ G_CALLBACK(gd_scroll_event), s);
g_signal_connect(s->drawing_area, "key-press-event",
G_CALLBACK(gd_key_event), s);
g_signal_connect(s->drawing_area, "key-release-event",
@@ -1447,7 +1496,7 @@ static const DisplayChangeListenerOps dcl_ops = {
.dpy_cursor_define = gd_cursor_define,
};
-void gtk_display_init(DisplayState *ds, bool full_screen)
+void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
{
GtkDisplayState *s = g_malloc0(sizeof(*s));
char *filename;
@@ -1526,6 +1575,9 @@ void gtk_display_init(DisplayState *ds, bool full_screen)
if (full_screen) {
gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
}
+ if (grab_on_hover) {
+ gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
+ }
register_displaychangelistener(&s->dcl);
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
new file mode 100644
index 000000000..1aa2605b7
--- /dev/null
+++ b/ui/input-legacy.c
@@ -0,0 +1,468 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * 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 "sysemu/sysemu.h"
+#include "monitor/monitor.h"
+#include "ui/console.h"
+#include "qapi/error.h"
+#include "qmp-commands.h"
+#include "qapi-types.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
+
+struct QEMUPutMouseEntry {
+ QEMUPutMouseEvent *qemu_put_mouse_event;
+ void *qemu_put_mouse_event_opaque;
+ int qemu_put_mouse_event_absolute;
+
+ /* new input core */
+ QemuInputHandler h;
+ QemuInputHandlerState *s;
+ int axis[INPUT_AXIS_MAX];
+ int buttons;
+};
+
+struct QEMUPutKbdEntry {
+ QEMUPutKBDEvent *put_kbd;
+ void *opaque;
+ QemuInputHandlerState *s;
+};
+
+struct QEMUPutLEDEntry {
+ QEMUPutLEDEvent *put_led;
+ void *opaque;
+ QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+};
+
+static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
+ QTAILQ_HEAD_INITIALIZER(led_handlers);
+static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
+ QTAILQ_HEAD_INITIALIZER(mouse_handlers);
+
+static const int key_defs[] = {
+ [Q_KEY_CODE_SHIFT] = 0x2a,
+ [Q_KEY_CODE_SHIFT_R] = 0x36,
+
+ [Q_KEY_CODE_ALT] = 0x38,
+ [Q_KEY_CODE_ALT_R] = 0xb8,
+ [Q_KEY_CODE_ALTGR] = 0x64,
+ [Q_KEY_CODE_ALTGR_R] = 0xe4,
+ [Q_KEY_CODE_CTRL] = 0x1d,
+ [Q_KEY_CODE_CTRL_R] = 0x9d,
+
+ [Q_KEY_CODE_MENU] = 0xdd,
+
+ [Q_KEY_CODE_ESC] = 0x01,
+
+ [Q_KEY_CODE_1] = 0x02,
+ [Q_KEY_CODE_2] = 0x03,
+ [Q_KEY_CODE_3] = 0x04,
+ [Q_KEY_CODE_4] = 0x05,
+ [Q_KEY_CODE_5] = 0x06,
+ [Q_KEY_CODE_6] = 0x07,
+ [Q_KEY_CODE_7] = 0x08,
+ [Q_KEY_CODE_8] = 0x09,
+ [Q_KEY_CODE_9] = 0x0a,
+ [Q_KEY_CODE_0] = 0x0b,
+ [Q_KEY_CODE_MINUS] = 0x0c,
+ [Q_KEY_CODE_EQUAL] = 0x0d,
+ [Q_KEY_CODE_BACKSPACE] = 0x0e,
+
+ [Q_KEY_CODE_TAB] = 0x0f,
+ [Q_KEY_CODE_Q] = 0x10,
+ [Q_KEY_CODE_W] = 0x11,
+ [Q_KEY_CODE_E] = 0x12,
+ [Q_KEY_CODE_R] = 0x13,
+ [Q_KEY_CODE_T] = 0x14,
+ [Q_KEY_CODE_Y] = 0x15,
+ [Q_KEY_CODE_U] = 0x16,
+ [Q_KEY_CODE_I] = 0x17,
+ [Q_KEY_CODE_O] = 0x18,
+ [Q_KEY_CODE_P] = 0x19,
+ [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
+ [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
+ [Q_KEY_CODE_RET] = 0x1c,
+
+ [Q_KEY_CODE_A] = 0x1e,
+ [Q_KEY_CODE_S] = 0x1f,
+ [Q_KEY_CODE_D] = 0x20,
+ [Q_KEY_CODE_F] = 0x21,
+ [Q_KEY_CODE_G] = 0x22,
+ [Q_KEY_CODE_H] = 0x23,
+ [Q_KEY_CODE_J] = 0x24,
+ [Q_KEY_CODE_K] = 0x25,
+ [Q_KEY_CODE_L] = 0x26,
+ [Q_KEY_CODE_SEMICOLON] = 0x27,
+ [Q_KEY_CODE_APOSTROPHE] = 0x28,
+ [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
+
+ [Q_KEY_CODE_BACKSLASH] = 0x2b,
+ [Q_KEY_CODE_Z] = 0x2c,
+ [Q_KEY_CODE_X] = 0x2d,
+ [Q_KEY_CODE_C] = 0x2e,
+ [Q_KEY_CODE_V] = 0x2f,
+ [Q_KEY_CODE_B] = 0x30,
+ [Q_KEY_CODE_N] = 0x31,
+ [Q_KEY_CODE_M] = 0x32,
+ [Q_KEY_CODE_COMMA] = 0x33,
+ [Q_KEY_CODE_DOT] = 0x34,
+ [Q_KEY_CODE_SLASH] = 0x35,
+
+ [Q_KEY_CODE_ASTERISK] = 0x37,
+
+ [Q_KEY_CODE_SPC] = 0x39,
+ [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
+ [Q_KEY_CODE_F1] = 0x3b,
+ [Q_KEY_CODE_F2] = 0x3c,
+ [Q_KEY_CODE_F3] = 0x3d,
+ [Q_KEY_CODE_F4] = 0x3e,
+ [Q_KEY_CODE_F5] = 0x3f,
+ [Q_KEY_CODE_F6] = 0x40,
+ [Q_KEY_CODE_F7] = 0x41,
+ [Q_KEY_CODE_F8] = 0x42,
+ [Q_KEY_CODE_F9] = 0x43,
+ [Q_KEY_CODE_F10] = 0x44,
+ [Q_KEY_CODE_NUM_LOCK] = 0x45,
+ [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
+
+ [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
+ [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
+ [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
+ [Q_KEY_CODE_KP_ADD] = 0x4e,
+ [Q_KEY_CODE_KP_ENTER] = 0x9c,
+ [Q_KEY_CODE_KP_DECIMAL] = 0x53,
+ [Q_KEY_CODE_SYSRQ] = 0x54,
+
+ [Q_KEY_CODE_KP_0] = 0x52,
+ [Q_KEY_CODE_KP_1] = 0x4f,
+ [Q_KEY_CODE_KP_2] = 0x50,
+ [Q_KEY_CODE_KP_3] = 0x51,
+ [Q_KEY_CODE_KP_4] = 0x4b,
+ [Q_KEY_CODE_KP_5] = 0x4c,
+ [Q_KEY_CODE_KP_6] = 0x4d,
+ [Q_KEY_CODE_KP_7] = 0x47,
+ [Q_KEY_CODE_KP_8] = 0x48,
+ [Q_KEY_CODE_KP_9] = 0x49,
+
+ [Q_KEY_CODE_LESS] = 0x56,
+
+ [Q_KEY_CODE_F11] = 0x57,
+ [Q_KEY_CODE_F12] = 0x58,
+
+ [Q_KEY_CODE_PRINT] = 0xb7,
+
+ [Q_KEY_CODE_HOME] = 0xc7,
+ [Q_KEY_CODE_PGUP] = 0xc9,
+ [Q_KEY_CODE_PGDN] = 0xd1,
+ [Q_KEY_CODE_END] = 0xcf,
+
+ [Q_KEY_CODE_LEFT] = 0xcb,
+ [Q_KEY_CODE_UP] = 0xc8,
+ [Q_KEY_CODE_DOWN] = 0xd0,
+ [Q_KEY_CODE_RIGHT] = 0xcd,
+
+ [Q_KEY_CODE_INSERT] = 0xd2,
+ [Q_KEY_CODE_DELETE] = 0xd3,
+#ifdef NEED_CPU_H
+#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
+ [Q_KEY_CODE_STOP] = 0xf0,
+ [Q_KEY_CODE_AGAIN] = 0xf1,
+ [Q_KEY_CODE_PROPS] = 0xf2,
+ [Q_KEY_CODE_UNDO] = 0xf3,
+ [Q_KEY_CODE_FRONT] = 0xf4,
+ [Q_KEY_CODE_COPY] = 0xf5,
+ [Q_KEY_CODE_OPEN] = 0xf6,
+ [Q_KEY_CODE_PASTE] = 0xf7,
+ [Q_KEY_CODE_FIND] = 0xf8,
+ [Q_KEY_CODE_CUT] = 0xf9,
+ [Q_KEY_CODE_LF] = 0xfa,
+ [Q_KEY_CODE_HELP] = 0xfb,
+ [Q_KEY_CODE_META_L] = 0xfc,
+ [Q_KEY_CODE_META_R] = 0xfd,
+ [Q_KEY_CODE_COMPOSE] = 0xfe,
+#endif
+#endif
+ [Q_KEY_CODE_MAX] = 0,
+};
+
+int index_from_key(const char *key)
+{
+ int i;
+
+ for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
+ if (!strcmp(key, QKeyCode_lookup[i])) {
+ break;
+ }
+ }
+
+ /* Return Q_KEY_CODE_MAX if the key is invalid */
+ return i;
+}
+
+static int *keycodes;
+static int keycodes_size;
+static QEMUTimer *key_timer;
+
+static int keycode_from_keyvalue(const KeyValue *value)
+{
+ if (value->kind == KEY_VALUE_KIND_QCODE) {
+ return key_defs[value->qcode];
+ } else {
+ assert(value->kind == KEY_VALUE_KIND_NUMBER);
+ return value->number;
+ }
+}
+
+static void free_keycodes(void)
+{
+ g_free(keycodes);
+ keycodes = NULL;
+ keycodes_size = 0;
+}
+
+static void release_keys(void *opaque)
+{
+ while (keycodes_size > 0) {
+ qemu_input_event_send_key_number(NULL, keycodes[--keycodes_size],
+ false);
+ }
+
+ free_keycodes();
+}
+
+void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
+ Error **errp)
+{
+ int keycode;
+ KeyValueList *p;
+
+ if (!key_timer) {
+ key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
+ }
+
+ if (keycodes != NULL) {
+ timer_del(key_timer);
+ release_keys(NULL);
+ }
+
+ if (!has_hold_time) {
+ hold_time = 100;
+ }
+
+ for (p = keys; p != NULL; p = p->next) {
+ /* key down events */
+ keycode = keycode_from_keyvalue(p->value);
+ if (keycode < 0x01 || keycode > 0xff) {
+ error_setg(errp, "invalid hex keycode 0x%x", keycode);
+ free_keycodes();
+ return;
+ }
+
+ qemu_input_event_send_key_number(NULL, keycode, true);
+
+ keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
+ keycodes[keycodes_size++] = keycode;
+ }
+
+ /* delayed key up events */
+ timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ muldiv64(get_ticks_per_sec(), hold_time, 1000));
+}
+
+static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
+ int keycode = keycode_from_keyvalue(evt->key->key);
+
+ if (!entry || !entry->put_kbd) {
+ return;
+ }
+ if (evt->key->key->kind == KEY_VALUE_KIND_QCODE &&
+ evt->key->key->qcode == Q_KEY_CODE_PAUSE) {
+ /* specific case */
+ int v = evt->key->down ? 0 : 0x80;
+ entry->put_kbd(entry->opaque, 0xe1);
+ entry->put_kbd(entry->opaque, 0x1d | v);
+ entry->put_kbd(entry->opaque, 0x45 | v);
+ return;
+ }
+ if (keycode & SCANCODE_GREY) {
+ entry->put_kbd(entry->opaque, SCANCODE_EMUL0);
+ keycode &= ~SCANCODE_GREY;
+ }
+ if (!evt->key->down) {
+ keycode |= SCANCODE_UP;
+ }
+ entry->put_kbd(entry->opaque, keycode);
+}
+
+static QemuInputHandler legacy_kbd_handler = {
+ .name = "legacy-kbd",
+ .mask = INPUT_EVENT_MASK_KEY,
+ .event = legacy_kbd_event,
+};
+
+QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+{
+ QEMUPutKbdEntry *entry;
+
+ entry = g_new0(QEMUPutKbdEntry, 1);
+ entry->put_kbd = func;
+ entry->opaque = opaque;
+ entry->s = qemu_input_handler_register((DeviceState *)entry,
+ &legacy_kbd_handler);
+ qemu_input_handler_activate(entry->s);
+ return entry;
+}
+
+void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+{
+ qemu_input_handler_unregister(entry->s);
+ g_free(entry);
+}
+
+static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ static const int bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
+ [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
+ [INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
+ };
+ QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+
+ switch (evt->kind) {
+ case INPUT_EVENT_KIND_BTN:
+ if (evt->btn->down) {
+ s->buttons |= bmap[evt->btn->button];
+ } else {
+ s->buttons &= ~bmap[evt->btn->button];
+ }
+ if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+ s->axis[INPUT_AXIS_X],
+ s->axis[INPUT_AXIS_Y],
+ -1,
+ s->buttons);
+ }
+ if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+ s->axis[INPUT_AXIS_X],
+ s->axis[INPUT_AXIS_Y],
+ 1,
+ s->buttons);
+ }
+ break;
+ case INPUT_EVENT_KIND_ABS:
+ s->axis[evt->abs->axis] = evt->abs->value;
+ break;
+ case INPUT_EVENT_KIND_REL:
+ s->axis[evt->rel->axis] += evt->rel->value;
+ break;
+ default:
+ break;
+ }
+}
+
+static void legacy_mouse_sync(DeviceState *dev)
+{
+ QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+
+ s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+ s->axis[INPUT_AXIS_X],
+ s->axis[INPUT_AXIS_Y],
+ 0,
+ s->buttons);
+
+ if (!s->qemu_put_mouse_event_absolute) {
+ s->axis[INPUT_AXIS_X] = 0;
+ s->axis[INPUT_AXIS_Y] = 0;
+ }
+}
+
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+ void *opaque, int absolute,
+ const char *name)
+{
+ QEMUPutMouseEntry *s;
+
+ s = g_malloc0(sizeof(QEMUPutMouseEntry));
+
+ s->qemu_put_mouse_event = func;
+ s->qemu_put_mouse_event_opaque = opaque;
+ s->qemu_put_mouse_event_absolute = absolute;
+
+ s->h.name = name;
+ s->h.mask = INPUT_EVENT_MASK_BTN |
+ (absolute ? INPUT_EVENT_MASK_ABS : INPUT_EVENT_MASK_REL);
+ s->h.event = legacy_mouse_event;
+ s->h.sync = legacy_mouse_sync;
+ s->s = qemu_input_handler_register((DeviceState *)s,
+ &s->h);
+
+ return s;
+}
+
+void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+ qemu_input_handler_activate(entry->s);
+}
+
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+ qemu_input_handler_unregister(entry->s);
+
+ g_free(entry);
+}
+
+QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
+ void *opaque)
+{
+ QEMUPutLEDEntry *s;
+
+ s = g_malloc0(sizeof(QEMUPutLEDEntry));
+
+ s->put_led = func;
+ s->opaque = opaque;
+ QTAILQ_INSERT_TAIL(&led_handlers, s, next);
+ return s;
+}
+
+void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+{
+ if (entry == NULL)
+ return;
+ QTAILQ_REMOVE(&led_handlers, entry, next);
+ g_free(entry);
+}
+
+void kbd_put_ledstate(int ledstate)
+{
+ QEMUPutLEDEntry *cursor;
+
+ QTAILQ_FOREACH(cursor, &led_handlers, next) {
+ cursor->put_led(cursor->opaque, ledstate);
+ }
+}
diff --git a/ui/input.c b/ui/input.c
index 92c44ca81..1ed0e783b 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -1,520 +1,336 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * 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 "sysemu/sysemu.h"
-#include "monitor/monitor.h"
-#include "ui/console.h"
-#include "qapi/error.h"
-#include "qmp-commands.h"
#include "qapi-types.h"
-#include "ui/keymaps.h"
-
-struct QEMUPutMouseEntry {
- QEMUPutMouseEvent *qemu_put_mouse_event;
- void *qemu_put_mouse_event_opaque;
- int qemu_put_mouse_event_absolute;
- char *qemu_put_mouse_event_name;
-
- int index;
-
- /* used internally by qemu for handling mice */
- QTAILQ_ENTRY(QEMUPutMouseEntry) node;
-};
-
-struct QEMUPutKbdEntry {
- QEMUPutKBDEvent *put_kbd;
- void *opaque;
- QTAILQ_ENTRY(QEMUPutKbdEntry) next;
-};
+#include "qmp-commands.h"
+#include "trace.h"
+#include "ui/input.h"
+#include "ui/console.h"
-struct QEMUPutLEDEntry {
- QEMUPutLEDEvent *put_led;
- void *opaque;
- QTAILQ_ENTRY(QEMUPutLEDEntry) next;
+struct QemuInputHandlerState {
+ DeviceState *dev;
+ QemuInputHandler *handler;
+ int id;
+ int events;
+ QTAILQ_ENTRY(QemuInputHandlerState) node;
};
-
-static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
- QTAILQ_HEAD_INITIALIZER(led_handlers);
-static QTAILQ_HEAD(, QEMUPutKbdEntry) kbd_handlers =
- QTAILQ_HEAD_INITIALIZER(kbd_handlers);
-static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
- QTAILQ_HEAD_INITIALIZER(mouse_handlers);
+static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
+ QTAILQ_HEAD_INITIALIZER(handlers);
static NotifierList mouse_mode_notifiers =
NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
-static const int key_defs[] = {
- [Q_KEY_CODE_SHIFT] = 0x2a,
- [Q_KEY_CODE_SHIFT_R] = 0x36,
-
- [Q_KEY_CODE_ALT] = 0x38,
- [Q_KEY_CODE_ALT_R] = 0xb8,
- [Q_KEY_CODE_ALTGR] = 0x64,
- [Q_KEY_CODE_ALTGR_R] = 0xe4,
- [Q_KEY_CODE_CTRL] = 0x1d,
- [Q_KEY_CODE_CTRL_R] = 0x9d,
-
- [Q_KEY_CODE_MENU] = 0xdd,
-
- [Q_KEY_CODE_ESC] = 0x01,
-
- [Q_KEY_CODE_1] = 0x02,
- [Q_KEY_CODE_2] = 0x03,
- [Q_KEY_CODE_3] = 0x04,
- [Q_KEY_CODE_4] = 0x05,
- [Q_KEY_CODE_5] = 0x06,
- [Q_KEY_CODE_6] = 0x07,
- [Q_KEY_CODE_7] = 0x08,
- [Q_KEY_CODE_8] = 0x09,
- [Q_KEY_CODE_9] = 0x0a,
- [Q_KEY_CODE_0] = 0x0b,
- [Q_KEY_CODE_MINUS] = 0x0c,
- [Q_KEY_CODE_EQUAL] = 0x0d,
- [Q_KEY_CODE_BACKSPACE] = 0x0e,
-
- [Q_KEY_CODE_TAB] = 0x0f,
- [Q_KEY_CODE_Q] = 0x10,
- [Q_KEY_CODE_W] = 0x11,
- [Q_KEY_CODE_E] = 0x12,
- [Q_KEY_CODE_R] = 0x13,
- [Q_KEY_CODE_T] = 0x14,
- [Q_KEY_CODE_Y] = 0x15,
- [Q_KEY_CODE_U] = 0x16,
- [Q_KEY_CODE_I] = 0x17,
- [Q_KEY_CODE_O] = 0x18,
- [Q_KEY_CODE_P] = 0x19,
- [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
- [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
- [Q_KEY_CODE_RET] = 0x1c,
-
- [Q_KEY_CODE_A] = 0x1e,
- [Q_KEY_CODE_S] = 0x1f,
- [Q_KEY_CODE_D] = 0x20,
- [Q_KEY_CODE_F] = 0x21,
- [Q_KEY_CODE_G] = 0x22,
- [Q_KEY_CODE_H] = 0x23,
- [Q_KEY_CODE_J] = 0x24,
- [Q_KEY_CODE_K] = 0x25,
- [Q_KEY_CODE_L] = 0x26,
- [Q_KEY_CODE_SEMICOLON] = 0x27,
- [Q_KEY_CODE_APOSTROPHE] = 0x28,
- [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
-
- [Q_KEY_CODE_BACKSLASH] = 0x2b,
- [Q_KEY_CODE_Z] = 0x2c,
- [Q_KEY_CODE_X] = 0x2d,
- [Q_KEY_CODE_C] = 0x2e,
- [Q_KEY_CODE_V] = 0x2f,
- [Q_KEY_CODE_B] = 0x30,
- [Q_KEY_CODE_N] = 0x31,
- [Q_KEY_CODE_M] = 0x32,
- [Q_KEY_CODE_COMMA] = 0x33,
- [Q_KEY_CODE_DOT] = 0x34,
- [Q_KEY_CODE_SLASH] = 0x35,
-
- [Q_KEY_CODE_ASTERISK] = 0x37,
-
- [Q_KEY_CODE_SPC] = 0x39,
- [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
- [Q_KEY_CODE_F1] = 0x3b,
- [Q_KEY_CODE_F2] = 0x3c,
- [Q_KEY_CODE_F3] = 0x3d,
- [Q_KEY_CODE_F4] = 0x3e,
- [Q_KEY_CODE_F5] = 0x3f,
- [Q_KEY_CODE_F6] = 0x40,
- [Q_KEY_CODE_F7] = 0x41,
- [Q_KEY_CODE_F8] = 0x42,
- [Q_KEY_CODE_F9] = 0x43,
- [Q_KEY_CODE_F10] = 0x44,
- [Q_KEY_CODE_NUM_LOCK] = 0x45,
- [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
-
- [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
- [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
- [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
- [Q_KEY_CODE_KP_ADD] = 0x4e,
- [Q_KEY_CODE_KP_ENTER] = 0x9c,
- [Q_KEY_CODE_KP_DECIMAL] = 0x53,
- [Q_KEY_CODE_SYSRQ] = 0x54,
-
- [Q_KEY_CODE_KP_0] = 0x52,
- [Q_KEY_CODE_KP_1] = 0x4f,
- [Q_KEY_CODE_KP_2] = 0x50,
- [Q_KEY_CODE_KP_3] = 0x51,
- [Q_KEY_CODE_KP_4] = 0x4b,
- [Q_KEY_CODE_KP_5] = 0x4c,
- [Q_KEY_CODE_KP_6] = 0x4d,
- [Q_KEY_CODE_KP_7] = 0x47,
- [Q_KEY_CODE_KP_8] = 0x48,
- [Q_KEY_CODE_KP_9] = 0x49,
-
- [Q_KEY_CODE_LESS] = 0x56,
-
- [Q_KEY_CODE_F11] = 0x57,
- [Q_KEY_CODE_F12] = 0x58,
-
- [Q_KEY_CODE_PRINT] = 0xb7,
-
- [Q_KEY_CODE_HOME] = 0xc7,
- [Q_KEY_CODE_PGUP] = 0xc9,
- [Q_KEY_CODE_PGDN] = 0xd1,
- [Q_KEY_CODE_END] = 0xcf,
-
- [Q_KEY_CODE_LEFT] = 0xcb,
- [Q_KEY_CODE_UP] = 0xc8,
- [Q_KEY_CODE_DOWN] = 0xd0,
- [Q_KEY_CODE_RIGHT] = 0xcd,
-
- [Q_KEY_CODE_INSERT] = 0xd2,
- [Q_KEY_CODE_DELETE] = 0xd3,
-#ifdef NEED_CPU_H
-#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
- [Q_KEY_CODE_STOP] = 0xf0,
- [Q_KEY_CODE_AGAIN] = 0xf1,
- [Q_KEY_CODE_PROPS] = 0xf2,
- [Q_KEY_CODE_UNDO] = 0xf3,
- [Q_KEY_CODE_FRONT] = 0xf4,
- [Q_KEY_CODE_COPY] = 0xf5,
- [Q_KEY_CODE_OPEN] = 0xf6,
- [Q_KEY_CODE_PASTE] = 0xf7,
- [Q_KEY_CODE_FIND] = 0xf8,
- [Q_KEY_CODE_CUT] = 0xf9,
- [Q_KEY_CODE_LF] = 0xfa,
- [Q_KEY_CODE_HELP] = 0xfb,
- [Q_KEY_CODE_META_L] = 0xfc,
- [Q_KEY_CODE_META_R] = 0xfd,
- [Q_KEY_CODE_COMPOSE] = 0xfe,
-#endif
-#endif
- [Q_KEY_CODE_MAX] = 0,
-};
-
-int index_from_key(const char *key)
+QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
+ QemuInputHandler *handler)
{
- int i;
+ QemuInputHandlerState *s = g_new0(QemuInputHandlerState, 1);
+ static int id = 1;
- for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
- if (!strcmp(key, QKeyCode_lookup[i])) {
- break;
- }
- }
+ s->dev = dev;
+ s->handler = handler;
+ s->id = id++;
+ QTAILQ_INSERT_TAIL(&handlers, s, node);
- /* Return Q_KEY_CODE_MAX if the key is invalid */
- return i;
+ qemu_input_check_mode_change();
+ return s;
}
-int index_from_keycode(int code)
+void qemu_input_handler_activate(QemuInputHandlerState *s)
{
- int i;
-
- for (i = 0; i < Q_KEY_CODE_MAX; i++) {
- if (key_defs[i] == code) {
- break;
- }
- }
-
- /* Return Q_KEY_CODE_MAX if the code is invalid */
- return i;
+ QTAILQ_REMOVE(&handlers, s, node);
+ QTAILQ_INSERT_HEAD(&handlers, s, node);
+ qemu_input_check_mode_change();
}
-static int *keycodes;
-static int keycodes_size;
-static QEMUTimer *key_timer;
-
-static int keycode_from_keyvalue(const KeyValue *value)
+void qemu_input_handler_unregister(QemuInputHandlerState *s)
{
- if (value->kind == KEY_VALUE_KIND_QCODE) {
- return key_defs[value->qcode];
- } else {
- assert(value->kind == KEY_VALUE_KIND_NUMBER);
- return value->number;
- }
+ QTAILQ_REMOVE(&handlers, s, node);
+ g_free(s);
+ qemu_input_check_mode_change();
}
-static void free_keycodes(void)
+static QemuInputHandlerState*
+qemu_input_find_handler(uint32_t mask)
{
- g_free(keycodes);
- keycodes = NULL;
- keycodes_size = 0;
-}
+ QemuInputHandlerState *s;
-static void release_keys(void *opaque)
-{
- while (keycodes_size > 0) {
- if (keycodes[--keycodes_size] & SCANCODE_GREY) {
- kbd_put_keycode(SCANCODE_EMUL0);
+ QTAILQ_FOREACH(s, &handlers, node) {
+ if (mask & s->handler->mask) {
+ return s;
}
- kbd_put_keycode(keycodes[keycodes_size] | SCANCODE_UP);
}
-
- free_keycodes();
+ return NULL;
}
-void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
- Error **errp)
+static void qemu_input_transform_abs_rotate(InputEvent *evt)
{
- int keycode;
- KeyValueList *p;
-
- if (!key_timer) {
- key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL);
- }
-
- if (keycodes != NULL) {
- qemu_del_timer(key_timer);
- release_keys(NULL);
- }
-
- if (!has_hold_time) {
- hold_time = 100;
- }
-
- for (p = keys; p != NULL; p = p->next) {
- /* key down events */
- keycode = keycode_from_keyvalue(p->value);
- if (keycode < 0x01 || keycode > 0xff) {
- error_setg(errp, "invalid hex keycode 0x%x", keycode);
- free_keycodes();
- return;
+ switch (graphic_rotate) {
+ case 90:
+ if (evt->abs->axis == INPUT_AXIS_X) {
+ evt->abs->axis = INPUT_AXIS_Y;
+ } else if (evt->abs->axis == INPUT_AXIS_Y) {
+ evt->abs->axis = INPUT_AXIS_X;
+ evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
}
-
- if (keycode & SCANCODE_GREY) {
- kbd_put_keycode(SCANCODE_EMUL0);
+ break;
+ case 180:
+ evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+ break;
+ case 270:
+ if (evt->abs->axis == INPUT_AXIS_X) {
+ evt->abs->axis = INPUT_AXIS_Y;
+ evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+ } else if (evt->abs->axis == INPUT_AXIS_Y) {
+ evt->abs->axis = INPUT_AXIS_X;
}
- kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
-
- keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
- keycodes[keycodes_size++] = keycode;
+ break;
}
-
- /* delayed key up events */
- qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) +
- muldiv64(get_ticks_per_sec(), hold_time, 1000));
}
-QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
{
- QEMUPutKbdEntry *entry;
+ const char *name;
+ int idx = -1;
- entry = g_malloc0(sizeof(QEMUPutKbdEntry));
- entry->put_kbd = func;
- entry->opaque = opaque;
- QTAILQ_INSERT_HEAD(&kbd_handlers, entry, next);
- return entry;
+ if (src) {
+ idx = qemu_console_get_index(src);
+ }
+ switch (evt->kind) {
+ case INPUT_EVENT_KIND_KEY:
+ switch (evt->key->key->kind) {
+ case KEY_VALUE_KIND_NUMBER:
+ trace_input_event_key_number(idx, evt->key->key->number,
+ evt->key->down);
+ break;
+ case KEY_VALUE_KIND_QCODE:
+ name = QKeyCode_lookup[evt->key->key->qcode];
+ trace_input_event_key_qcode(idx, name, evt->key->down);
+ break;
+ case KEY_VALUE_KIND_MAX:
+ /* keep gcc happy */
+ break;
+ }
+ break;
+ case INPUT_EVENT_KIND_BTN:
+ name = InputButton_lookup[evt->btn->button];
+ trace_input_event_btn(idx, name, evt->btn->down);
+ break;
+ case INPUT_EVENT_KIND_REL:
+ name = InputAxis_lookup[evt->rel->axis];
+ trace_input_event_rel(idx, name, evt->rel->value);
+ break;
+ case INPUT_EVENT_KIND_ABS:
+ name = InputAxis_lookup[evt->abs->axis];
+ trace_input_event_abs(idx, name, evt->abs->value);
+ break;
+ case INPUT_EVENT_KIND_MAX:
+ /* keep gcc happy */
+ break;
+ }
}
-void qemu_remove_kbd_event_handler(QEMUPutKbdEntry *entry)
+void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
{
- QTAILQ_REMOVE(&kbd_handlers, entry, next);
-}
+ QemuInputHandlerState *s;
-static void check_mode_change(void)
-{
- static int current_is_absolute, current_has_absolute;
- int is_absolute;
- int has_absolute;
+ if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+ return;
+ }
- is_absolute = kbd_mouse_is_absolute();
- has_absolute = kbd_mouse_has_absolute();
+ qemu_input_event_trace(src, evt);
- if (is_absolute != current_is_absolute ||
- has_absolute != current_has_absolute) {
- notifier_list_notify(&mouse_mode_notifiers, NULL);
+ /* pre processing */
+ if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) {
+ qemu_input_transform_abs_rotate(evt);
}
- current_is_absolute = is_absolute;
- current_has_absolute = has_absolute;
+ /* send event */
+ s = qemu_input_find_handler(1 << evt->kind);
+ if (!s) {
+ return;
+ }
+ s->handler->event(s->dev, src, evt);
+ s->events++;
}
-QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
- void *opaque, int absolute,
- const char *name)
+void qemu_input_event_sync(void)
{
- QEMUPutMouseEntry *s;
- static int mouse_index = 0;
-
- s = g_malloc0(sizeof(QEMUPutMouseEntry));
-
- s->qemu_put_mouse_event = func;
- s->qemu_put_mouse_event_opaque = opaque;
- s->qemu_put_mouse_event_absolute = absolute;
- s->qemu_put_mouse_event_name = g_strdup(name);
- s->index = mouse_index++;
+ QemuInputHandlerState *s;
- QTAILQ_INSERT_TAIL(&mouse_handlers, s, node);
+ if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+ return;
+ }
- check_mode_change();
+ trace_input_event_sync();
- return s;
+ QTAILQ_FOREACH(s, &handlers, node) {
+ if (!s->events) {
+ continue;
+ }
+ if (s->handler->sync) {
+ s->handler->sync(s->dev);
+ }
+ s->events = 0;
+ }
}
-void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry)
+InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
{
- QTAILQ_REMOVE(&mouse_handlers, entry, node);
- QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node);
-
- check_mode_change();
+ InputEvent *evt = g_new0(InputEvent, 1);
+ evt->key = g_new0(InputKeyEvent, 1);
+ evt->kind = INPUT_EVENT_KIND_KEY;
+ evt->key->key = key;
+ evt->key->down = down;
+ return evt;
}
-void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
{
- QTAILQ_REMOVE(&mouse_handlers, entry, node);
-
- g_free(entry->qemu_put_mouse_event_name);
- g_free(entry);
-
- check_mode_change();
+ InputEvent *evt;
+ evt = qemu_input_event_new_key(key, down);
+ qemu_input_event_send(src, evt);
+ qemu_input_event_sync();
+ qapi_free_InputEvent(evt);
}
-QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
- void *opaque)
+void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
{
- QEMUPutLEDEntry *s;
+ KeyValue *key = g_new0(KeyValue, 1);
+ key->kind = KEY_VALUE_KIND_NUMBER;
+ key->number = num;
+ qemu_input_event_send_key(src, key, down);
+}
- s = g_malloc0(sizeof(QEMUPutLEDEntry));
+void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
+{
+ KeyValue *key = g_new0(KeyValue, 1);
+ key->kind = KEY_VALUE_KIND_QCODE;
+ key->qcode = q;
+ qemu_input_event_send_key(src, key, down);
+}
- s->put_led = func;
- s->opaque = opaque;
- QTAILQ_INSERT_TAIL(&led_handlers, s, next);
- return s;
+InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
+{
+ InputEvent *evt = g_new0(InputEvent, 1);
+ evt->btn = g_new0(InputBtnEvent, 1);
+ evt->kind = INPUT_EVENT_KIND_BTN;
+ evt->btn->button = btn;
+ evt->btn->down = down;
+ return evt;
}
-void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry)
+void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down)
{
- if (entry == NULL)
- return;
- QTAILQ_REMOVE(&led_handlers, entry, next);
- g_free(entry);
+ InputEvent *evt;
+ evt = qemu_input_event_new_btn(btn, down);
+ qemu_input_event_send(src, evt);
+ qapi_free_InputEvent(evt);
}
-void kbd_put_keycode(int keycode)
+void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
+ uint32_t button_old, uint32_t button_new)
{
- QEMUPutKbdEntry *entry = QTAILQ_FIRST(&kbd_handlers);
+ InputButton btn;
+ uint32_t mask;
- if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
- return;
- }
- if (entry) {
- entry->put_kbd(entry->opaque, keycode);
+ for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) {
+ mask = button_map[btn];
+ if ((button_old & mask) == (button_new & mask)) {
+ continue;
+ }
+ qemu_input_queue_btn(src, btn, button_new & mask);
}
}
-void kbd_put_ledstate(int ledstate)
+bool qemu_input_is_absolute(void)
{
- QEMUPutLEDEntry *cursor;
+ QemuInputHandlerState *s;
- QTAILQ_FOREACH(cursor, &led_handlers, next) {
- cursor->put_led(cursor->opaque, ledstate);
- }
+ s = qemu_input_find_handler(INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS);
+ return (s != NULL) && (s->handler->mask & INPUT_EVENT_MASK_ABS);
}
-void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
+int qemu_input_scale_axis(int value, int size_in, int size_out)
{
- QEMUPutMouseEntry *entry;
- QEMUPutMouseEvent *mouse_event;
- void *mouse_event_opaque;
- int width, height;
-
- if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
- return;
+ if (size_in < 2) {
+ return size_out / 2;
}
- if (QTAILQ_EMPTY(&mouse_handlers)) {
- return;
- }
-
- entry = QTAILQ_FIRST(&mouse_handlers);
+ return (int64_t)value * (size_out - 1) / (size_in - 1);
+}
- mouse_event = entry->qemu_put_mouse_event;
- mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
+InputEvent *qemu_input_event_new_move(InputEventKind kind,
+ InputAxis axis, int value)
+{
+ InputEvent *evt = g_new0(InputEvent, 1);
+ InputMoveEvent *move = g_new0(InputMoveEvent, 1);
+
+ evt->kind = kind;
+ evt->data = move;
+ move->axis = axis;
+ move->value = value;
+ return evt;
+}
- if (mouse_event) {
- if (entry->qemu_put_mouse_event_absolute) {
- width = 0x7fff;
- height = 0x7fff;
- } else {
- width = graphic_width - 1;
- height = graphic_height - 1;
- }
+void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
+{
+ InputEvent *evt;
+ evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value);
+ qemu_input_event_send(src, evt);
+ qapi_free_InputEvent(evt);
+}
- switch (graphic_rotate) {
- case 0:
- mouse_event(mouse_event_opaque,
- dx, dy, dz, buttons_state);
- break;
- case 90:
- mouse_event(mouse_event_opaque,
- width - dy, dx, dz, buttons_state);
- break;
- case 180:
- mouse_event(mouse_event_opaque,
- width - dx, height - dy, dz, buttons_state);
- break;
- case 270:
- mouse_event(mouse_event_opaque,
- dy, height - dx, dz, buttons_state);
- break;
- }
- }
+void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int size)
+{
+ InputEvent *evt;
+ int scaled = qemu_input_scale_axis(value, size, INPUT_EVENT_ABS_SIZE);
+ evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled);
+ qemu_input_event_send(src, evt);
+ qapi_free_InputEvent(evt);
}
-int kbd_mouse_is_absolute(void)
+void qemu_input_check_mode_change(void)
{
- if (QTAILQ_EMPTY(&mouse_handlers)) {
- return 0;
+ static int current_is_absolute;
+ int is_absolute;
+
+ is_absolute = qemu_input_is_absolute();
+
+ if (is_absolute != current_is_absolute) {
+ trace_input_mouse_mode(is_absolute);
+ notifier_list_notify(&mouse_mode_notifiers, NULL);
}
- return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute;
+ current_is_absolute = is_absolute;
}
-int kbd_mouse_has_absolute(void)
+void qemu_add_mouse_mode_change_notifier(Notifier *notify)
{
- QEMUPutMouseEntry *entry;
-
- QTAILQ_FOREACH(entry, &mouse_handlers, node) {
- if (entry->qemu_put_mouse_event_absolute) {
- return 1;
- }
- }
+ notifier_list_add(&mouse_mode_notifiers, notify);
+}
- return 0;
+void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
+{
+ notifier_remove(notify);
}
MouseInfoList *qmp_query_mice(Error **errp)
{
MouseInfoList *mice_list = NULL;
- QEMUPutMouseEntry *cursor;
+ MouseInfoList *info;
+ QemuInputHandlerState *s;
bool current = true;
- QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
- MouseInfoList *info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(cursor->qemu_put_mouse_event_name);
- info->value->index = cursor->index;
- info->value->absolute = !!cursor->qemu_put_mouse_event_absolute;
+ QTAILQ_FOREACH(s, &handlers, node) {
+ if (!(s->handler->mask &
+ (INPUT_EVENT_MASK_REL | INPUT_EVENT_MASK_ABS))) {
+ continue;
+ }
+
+ info = g_new0(MouseInfoList, 1);
+ info->value = g_new0(MouseInfo, 1);
+ info->value->index = s->id;
+ info->value->name = g_strdup(s->handler->name);
+ info->value->absolute = s->handler->mask & INPUT_EVENT_MASK_ABS;
info->value->current = current;
current = false;
-
info->next = mice_list;
mice_list = info;
}
@@ -524,36 +340,27 @@ MouseInfoList *qmp_query_mice(Error **errp)
void do_mouse_set(Monitor *mon, const QDict *qdict)
{
- QEMUPutMouseEntry *cursor;
+ QemuInputHandlerState *s;
int index = qdict_get_int(qdict, "index");
int found = 0;
- if (QTAILQ_EMPTY(&mouse_handlers)) {
- monitor_printf(mon, "No mouse devices connected\n");
- return;
- }
-
- QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
- if (cursor->index == index) {
- found = 1;
- qemu_activate_mouse_event_handler(cursor);
- break;
+ QTAILQ_FOREACH(s, &handlers, node) {
+ if (s->id != index) {
+ continue;
}
+ if (!(s->handler->mask & (INPUT_EVENT_MASK_REL |
+ INPUT_EVENT_MASK_ABS))) {
+ error_report("Input device '%s' is not a mouse", s->handler->name);
+ return;
+ }
+ found = 1;
+ qemu_input_handler_activate(s);
+ break;
}
if (!found) {
- monitor_printf(mon, "Mouse at given index not found\n");
+ error_report("Mouse at index '%d' not found", index);
}
- check_mode_change();
-}
-
-void qemu_add_mouse_mode_change_notifier(Notifier *notify)
-{
- notifier_list_add(&mouse_mode_notifiers, notify);
-}
-
-void qemu_remove_mouse_mode_change_notifier(Notifier *notify)
-{
- notifier_remove(notify);
+ qemu_input_check_mode_change();
}
diff --git a/ui/keymaps.c b/ui/keymaps.c
index f373cc53d..80d658d90 100644
--- a/ui/keymaps.c
+++ b/ui/keymaps.c
@@ -33,6 +33,12 @@ static int get_keysym(const name2keysym_t *table,
if (!strcmp(p->name, name))
return p->keysym;
}
+ if (name[0] == 'U' && strlen(name) == 5) { /* try unicode Uxxxx */
+ char *end;
+ int ret = (int)strtoul(name + 1, &end, 16);
+ if (*end == '\0' && ret > 0)
+ return ret;
+ }
return 0;
}
diff --git a/ui/sdl.c b/ui/sdl.c
index 39a42d6b0..4e7f920e3 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -26,10 +26,13 @@
#undef WIN32_LEAN_AND_MEAN
#include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 1
#include <SDL_syswm.h>
#include "qemu-common.h"
#include "ui/console.h"
+#include "ui/input.h"
#include "sysemu/sysemu.h"
#include "x_keymap.h"
#include "sdl_zoom.h"
@@ -86,6 +89,7 @@ static void sdl_update(DisplayChangeListener *dcl,
static void do_sdl_resize(int width, int height, int bpp)
{
int flags;
+ SDL_Surface *tmp_screen;
// printf("resizing to %d %d\n", w, h);
@@ -98,12 +102,26 @@ static void do_sdl_resize(int width, int height, int bpp)
if (gui_noframe)
flags |= SDL_NOFRAME;
- real_screen = SDL_SetVideoMode(width, height, bpp, flags);
+ tmp_screen = SDL_SetVideoMode(width, height, bpp, flags);
if (!real_screen) {
- fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width,
- height, bpp, SDL_GetError());
- exit(1);
+ if (!tmp_screen) {
+ fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n",
+ width, height, bpp, SDL_GetError());
+ exit(1);
+ }
+ } else {
+ /*
+ * Revert to the previous video mode if the change of resizing or
+ * resolution failed.
+ */
+ if (!tmp_screen) {
+ fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n",
+ width, height, bpp, SDL_GetError());
+ return;
+ }
}
+
+ real_screen = tmp_screen;
}
static void sdl_switch(DisplayChangeListener *dcl,
@@ -246,9 +264,7 @@ static void reset_keys(void)
int i;
for(i = 0; i < 256; i++) {
if (modifiers_state[i]) {
- if (i & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(i | SCANCODE_UP);
+ qemu_input_event_send_key_number(dcl->con, i, false);
modifiers_state[i] = 0;
}
}
@@ -256,16 +272,12 @@ static void reset_keys(void)
static void sdl_process_key(SDL_KeyboardEvent *ev)
{
- int keycode, v;
+ int keycode;
if (ev->keysym.sym == SDLK_PAUSE) {
/* specific case */
- v = 0;
- if (ev->type == SDL_KEYUP)
- v |= SCANCODE_UP;
- kbd_put_keycode(0xe1);
- kbd_put_keycode(0x1d | v);
- kbd_put_keycode(0x45 | v);
+ qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE,
+ ev->type == SDL_KEYDOWN);
return;
}
@@ -297,19 +309,15 @@ static void sdl_process_key(SDL_KeyboardEvent *ev)
case 0x45: /* num lock */
case 0x3a: /* caps lock */
/* SDL does not send the key up event, so we generate it */
- kbd_put_keycode(keycode);
- kbd_put_keycode(keycode | SCANCODE_UP);
+ qemu_input_event_send_key_number(dcl->con, keycode, true);
+ qemu_input_event_send_key_number(dcl->con, keycode, false);
return;
#endif
}
/* now send the key code */
- if (keycode & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- if (ev->type == SDL_KEYUP)
- kbd_put_keycode(keycode | SCANCODE_UP);
- else
- kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+ qemu_input_event_send_key_number(dcl->con, keycode,
+ ev->type == SDL_KEYDOWN);
}
static void sdl_update_caption(void)
@@ -345,7 +353,7 @@ static void sdl_hide_cursor(void)
if (!cursor_hide)
return;
- if (kbd_mouse_is_absolute()) {
+ if (qemu_input_is_absolute()) {
SDL_ShowCursor(1);
SDL_SetCursor(sdl_cursor_hidden);
} else {
@@ -358,10 +366,10 @@ static void sdl_show_cursor(void)
if (!cursor_hide)
return;
- if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) {
+ if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) {
SDL_ShowCursor(1);
if (guest_cursor &&
- (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ (gui_grab || qemu_input_is_absolute() || absolute_enabled))
SDL_SetCursor(guest_sprite);
else
SDL_SetCursor(sdl_cursor_normal);
@@ -380,8 +388,9 @@ static void sdl_grab_start(void)
}
if (guest_cursor) {
SDL_SetCursor(guest_sprite);
- if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ if (!qemu_input_is_absolute() && !absolute_enabled) {
SDL_WarpMouse(guest_x, guest_y);
+ }
} else
sdl_hide_cursor();
SDL_WM_GrabInput(SDL_GRAB_ON);
@@ -410,7 +419,7 @@ static void absolute_mouse_grab(void)
static void sdl_mouse_mode_change(Notifier *notify, void *data)
{
- if (kbd_mouse_is_absolute()) {
+ if (qemu_input_is_absolute()) {
if (!absolute_enabled) {
absolute_enabled = 1;
if (qemu_console_is_graphic(NULL)) {
@@ -425,33 +434,40 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
}
}
-static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
{
- int buttons = 0;
-
- if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
- buttons |= MOUSE_EVENT_LBUTTON;
- }
- if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
- buttons |= MOUSE_EVENT_RBUTTON;
- }
- if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) {
- buttons |= MOUSE_EVENT_MBUTTON;
- }
-
- if (kbd_mouse_is_absolute()) {
- dx = x * 0x7FFF / (real_screen->w - 1);
- dy = y * 0x7FFF / (real_screen->h - 1);
- } else if (guest_cursor) {
- x -= guest_x;
- y -= guest_y;
- guest_x += x;
- guest_y += y;
- dx = x;
- dy = y;
+ static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
+ [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
+ [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
+ [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
+ [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
+ };
+ static uint32_t prev_state;
+
+ if (prev_state != state) {
+ qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
+ prev_state = state;
+ }
+
+ if (qemu_input_is_absolute()) {
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
+ real_screen->w);
+ qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
+ real_screen->h);
+ } else {
+ if (guest_cursor) {
+ x -= guest_x;
+ y -= guest_y;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+ qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, dx);
+ qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, dy);
}
-
- kbd_mouse_event(dx, dy, dz, buttons);
+ qemu_input_event_sync();
}
static void sdl_scale(int width, int height)
@@ -679,7 +695,7 @@ static void handle_mousemotion(SDL_Event *ev)
int max_x, max_y;
if (qemu_console_is_graphic(NULL) &&
- (kbd_mouse_is_absolute() || absolute_enabled)) {
+ (qemu_input_is_absolute() || absolute_enabled)) {
max_x = real_screen->w - 1;
max_y = real_screen->h - 1;
if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
@@ -692,8 +708,8 @@ static void handle_mousemotion(SDL_Event *ev)
sdl_grab_start();
}
}
- if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
- sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+ if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+ sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel,
ev->motion.x, ev->motion.y, ev->motion.state);
}
}
@@ -702,35 +718,24 @@ static void handle_mousebutton(SDL_Event *ev)
{
int buttonstate = SDL_GetMouseState(NULL, NULL);
SDL_MouseButtonEvent *bev;
- int dz;
if (!qemu_console_is_graphic(NULL)) {
return;
}
bev = &ev->button;
- if (!gui_grab && !kbd_mouse_is_absolute()) {
+ if (!gui_grab && !qemu_input_is_absolute()) {
if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
/* start grabbing all events */
sdl_grab_start();
}
} else {
- dz = 0;
if (ev->type == SDL_MOUSEBUTTONDOWN) {
buttonstate |= SDL_BUTTON(bev->button);
} else {
buttonstate &= ~SDL_BUTTON(bev->button);
}
-#ifdef SDL_BUTTON_WHEELUP
- if (bev->button == SDL_BUTTON_WHEELUP &&
- ev->type == SDL_MOUSEBUTTONDOWN) {
- dz = -1;
- } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
- ev->type == SDL_MOUSEBUTTONDOWN) {
- dz = 1;
- }
-#endif
- sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+ sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate);
}
}
@@ -745,7 +750,7 @@ static void handle_activation(SDL_Event *ev)
}
#endif
if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
- (kbd_mouse_is_absolute() || absolute_enabled)) {
+ (qemu_input_is_absolute() || absolute_enabled)) {
absolute_mouse_grab();
}
if (ev->active.state & SDL_APPACTIVE) {
@@ -817,10 +822,11 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl,
if (on) {
if (!guest_cursor)
sdl_show_cursor();
- if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
SDL_SetCursor(guest_sprite);
- if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ if (!qemu_input_is_absolute() && !absolute_enabled) {
SDL_WarpMouse(x, y);
+ }
}
} else if (gui_grab)
sdl_hide_cursor();
@@ -848,7 +854,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl,
g_free(mask);
if (guest_cursor &&
- (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ (gui_grab || qemu_input_is_absolute() || absolute_enabled))
SDL_SetCursor(guest_sprite);
}
@@ -951,3 +957,4 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
atexit(sdl_cleanup);
}
+#endif
diff --git a/ui/sdl2-keymap.h b/ui/sdl2-keymap.h
new file mode 100644
index 000000000..5a12f4543
--- /dev/null
+++ b/ui/sdl2-keymap.h
@@ -0,0 +1,266 @@
+
+/* map SDL2 scancodes to QKeyCode */
+
+static const int sdl2_scancode_to_qcode[SDL_NUM_SCANCODES] = {
+ [SDL_SCANCODE_A] = Q_KEY_CODE_A,
+ [SDL_SCANCODE_B] = Q_KEY_CODE_B,
+ [SDL_SCANCODE_C] = Q_KEY_CODE_C,
+ [SDL_SCANCODE_D] = Q_KEY_CODE_D,
+ [SDL_SCANCODE_E] = Q_KEY_CODE_E,
+ [SDL_SCANCODE_F] = Q_KEY_CODE_F,
+ [SDL_SCANCODE_G] = Q_KEY_CODE_G,
+ [SDL_SCANCODE_H] = Q_KEY_CODE_H,
+ [SDL_SCANCODE_I] = Q_KEY_CODE_I,
+ [SDL_SCANCODE_J] = Q_KEY_CODE_J,
+ [SDL_SCANCODE_K] = Q_KEY_CODE_K,
+ [SDL_SCANCODE_L] = Q_KEY_CODE_L,
+ [SDL_SCANCODE_M] = Q_KEY_CODE_M,
+ [SDL_SCANCODE_N] = Q_KEY_CODE_N,
+ [SDL_SCANCODE_O] = Q_KEY_CODE_O,
+ [SDL_SCANCODE_P] = Q_KEY_CODE_P,
+ [SDL_SCANCODE_Q] = Q_KEY_CODE_Q,
+ [SDL_SCANCODE_R] = Q_KEY_CODE_R,
+ [SDL_SCANCODE_S] = Q_KEY_CODE_S,
+ [SDL_SCANCODE_T] = Q_KEY_CODE_T,
+ [SDL_SCANCODE_U] = Q_KEY_CODE_U,
+ [SDL_SCANCODE_V] = Q_KEY_CODE_V,
+ [SDL_SCANCODE_W] = Q_KEY_CODE_W,
+ [SDL_SCANCODE_X] = Q_KEY_CODE_X,
+ [SDL_SCANCODE_Y] = Q_KEY_CODE_Y,
+ [SDL_SCANCODE_Z] = Q_KEY_CODE_Z,
+
+ [SDL_SCANCODE_1] = Q_KEY_CODE_1,
+ [SDL_SCANCODE_2] = Q_KEY_CODE_2,
+ [SDL_SCANCODE_3] = Q_KEY_CODE_3,
+ [SDL_SCANCODE_4] = Q_KEY_CODE_4,
+ [SDL_SCANCODE_5] = Q_KEY_CODE_5,
+ [SDL_SCANCODE_6] = Q_KEY_CODE_6,
+ [SDL_SCANCODE_7] = Q_KEY_CODE_7,
+ [SDL_SCANCODE_8] = Q_KEY_CODE_8,
+ [SDL_SCANCODE_9] = Q_KEY_CODE_9,
+ [SDL_SCANCODE_0] = Q_KEY_CODE_0,
+
+ [SDL_SCANCODE_RETURN] = Q_KEY_CODE_RET,
+ [SDL_SCANCODE_ESCAPE] = Q_KEY_CODE_ESC,
+ [SDL_SCANCODE_BACKSPACE] = Q_KEY_CODE_BACKSPACE,
+ [SDL_SCANCODE_TAB] = Q_KEY_CODE_TAB,
+ [SDL_SCANCODE_SPACE] = Q_KEY_CODE_SPC,
+ [SDL_SCANCODE_MINUS] = Q_KEY_CODE_MINUS,
+ [SDL_SCANCODE_EQUALS] = Q_KEY_CODE_EQUAL,
+ [SDL_SCANCODE_LEFTBRACKET] = Q_KEY_CODE_BRACKET_LEFT,
+ [SDL_SCANCODE_RIGHTBRACKET] = Q_KEY_CODE_BRACKET_RIGHT,
+ [SDL_SCANCODE_BACKSLASH] = Q_KEY_CODE_BACKSLASH,
+#if 0
+ [SDL_SCANCODE_NONUSHASH] = Q_KEY_CODE_NONUSHASH,
+#endif
+ [SDL_SCANCODE_SEMICOLON] = Q_KEY_CODE_SEMICOLON,
+ [SDL_SCANCODE_APOSTROPHE] = Q_KEY_CODE_APOSTROPHE,
+ [SDL_SCANCODE_GRAVE] = Q_KEY_CODE_GRAVE_ACCENT,
+ [SDL_SCANCODE_COMMA] = Q_KEY_CODE_COMMA,
+ [SDL_SCANCODE_PERIOD] = Q_KEY_CODE_DOT,
+ [SDL_SCANCODE_SLASH] = Q_KEY_CODE_SLASH,
+ [SDL_SCANCODE_CAPSLOCK] = Q_KEY_CODE_CAPS_LOCK,
+
+ [SDL_SCANCODE_F1] = Q_KEY_CODE_F1,
+ [SDL_SCANCODE_F2] = Q_KEY_CODE_F2,
+ [SDL_SCANCODE_F3] = Q_KEY_CODE_F3,
+ [SDL_SCANCODE_F4] = Q_KEY_CODE_F4,
+ [SDL_SCANCODE_F5] = Q_KEY_CODE_F5,
+ [SDL_SCANCODE_F6] = Q_KEY_CODE_F6,
+ [SDL_SCANCODE_F7] = Q_KEY_CODE_F7,
+ [SDL_SCANCODE_F8] = Q_KEY_CODE_F8,
+ [SDL_SCANCODE_F9] = Q_KEY_CODE_F9,
+ [SDL_SCANCODE_F10] = Q_KEY_CODE_F10,
+ [SDL_SCANCODE_F11] = Q_KEY_CODE_F11,
+ [SDL_SCANCODE_F12] = Q_KEY_CODE_F12,
+
+ [SDL_SCANCODE_PRINTSCREEN] = Q_KEY_CODE_PRINT,
+ [SDL_SCANCODE_SCROLLLOCK] = Q_KEY_CODE_SCROLL_LOCK,
+ [SDL_SCANCODE_PAUSE] = Q_KEY_CODE_PAUSE,
+ [SDL_SCANCODE_INSERT] = Q_KEY_CODE_INSERT,
+ [SDL_SCANCODE_HOME] = Q_KEY_CODE_HOME,
+ [SDL_SCANCODE_PAGEUP] = Q_KEY_CODE_PGUP,
+ [SDL_SCANCODE_DELETE] = Q_KEY_CODE_DELETE,
+ [SDL_SCANCODE_END] = Q_KEY_CODE_END,
+ [SDL_SCANCODE_PAGEDOWN] = Q_KEY_CODE_PGDN,
+ [SDL_SCANCODE_RIGHT] = Q_KEY_CODE_RIGHT,
+ [SDL_SCANCODE_LEFT] = Q_KEY_CODE_LEFT,
+ [SDL_SCANCODE_DOWN] = Q_KEY_CODE_DOWN,
+ [SDL_SCANCODE_UP] = Q_KEY_CODE_UP,
+ [SDL_SCANCODE_NUMLOCKCLEAR] = Q_KEY_CODE_NUM_LOCK,
+
+ [SDL_SCANCODE_KP_DIVIDE] = Q_KEY_CODE_KP_DIVIDE,
+ [SDL_SCANCODE_KP_MULTIPLY] = Q_KEY_CODE_KP_MULTIPLY,
+ [SDL_SCANCODE_KP_MINUS] = Q_KEY_CODE_KP_SUBTRACT,
+ [SDL_SCANCODE_KP_PLUS] = Q_KEY_CODE_KP_ADD,
+ [SDL_SCANCODE_KP_ENTER] = Q_KEY_CODE_KP_ENTER,
+ [SDL_SCANCODE_KP_1] = Q_KEY_CODE_KP_1,
+ [SDL_SCANCODE_KP_2] = Q_KEY_CODE_KP_2,
+ [SDL_SCANCODE_KP_3] = Q_KEY_CODE_KP_3,
+ [SDL_SCANCODE_KP_4] = Q_KEY_CODE_KP_4,
+ [SDL_SCANCODE_KP_5] = Q_KEY_CODE_KP_5,
+ [SDL_SCANCODE_KP_6] = Q_KEY_CODE_KP_6,
+ [SDL_SCANCODE_KP_7] = Q_KEY_CODE_KP_7,
+ [SDL_SCANCODE_KP_8] = Q_KEY_CODE_KP_8,
+ [SDL_SCANCODE_KP_9] = Q_KEY_CODE_KP_9,
+ [SDL_SCANCODE_KP_0] = Q_KEY_CODE_KP_0,
+ [SDL_SCANCODE_KP_PERIOD] = Q_KEY_CODE_KP_DECIMAL,
+#if 0
+ [SDL_SCANCODE_NONUSBACKSLASH] = Q_KEY_CODE_NONUSBACKSLASH,
+ [SDL_SCANCODE_APPLICATION] = Q_KEY_CODE_APPLICATION,
+ [SDL_SCANCODE_POWER] = Q_KEY_CODE_POWER,
+ [SDL_SCANCODE_KP_EQUALS] = Q_KEY_CODE_KP_EQUALS,
+
+ [SDL_SCANCODE_F13] = Q_KEY_CODE_F13,
+ [SDL_SCANCODE_F14] = Q_KEY_CODE_F14,
+ [SDL_SCANCODE_F15] = Q_KEY_CODE_F15,
+ [SDL_SCANCODE_F16] = Q_KEY_CODE_F16,
+ [SDL_SCANCODE_F17] = Q_KEY_CODE_F17,
+ [SDL_SCANCODE_F18] = Q_KEY_CODE_F18,
+ [SDL_SCANCODE_F19] = Q_KEY_CODE_F19,
+ [SDL_SCANCODE_F20] = Q_KEY_CODE_F20,
+ [SDL_SCANCODE_F21] = Q_KEY_CODE_F21,
+ [SDL_SCANCODE_F22] = Q_KEY_CODE_F22,
+ [SDL_SCANCODE_F23] = Q_KEY_CODE_F23,
+ [SDL_SCANCODE_F24] = Q_KEY_CODE_F24,
+
+ [SDL_SCANCODE_EXECUTE] = Q_KEY_CODE_EXECUTE,
+#endif
+ [SDL_SCANCODE_HELP] = Q_KEY_CODE_HELP,
+ [SDL_SCANCODE_MENU] = Q_KEY_CODE_MENU,
+#if 0
+ [SDL_SCANCODE_SELECT] = Q_KEY_CODE_SELECT,
+#endif
+ [SDL_SCANCODE_STOP] = Q_KEY_CODE_STOP,
+ [SDL_SCANCODE_AGAIN] = Q_KEY_CODE_AGAIN,
+ [SDL_SCANCODE_UNDO] = Q_KEY_CODE_UNDO,
+ [SDL_SCANCODE_CUT] = Q_KEY_CODE_CUT,
+ [SDL_SCANCODE_COPY] = Q_KEY_CODE_COPY,
+ [SDL_SCANCODE_PASTE] = Q_KEY_CODE_PASTE,
+ [SDL_SCANCODE_FIND] = Q_KEY_CODE_FIND,
+#if 0
+ [SDL_SCANCODE_MUTE] = Q_KEY_CODE_MUTE,
+ [SDL_SCANCODE_VOLUMEUP] = Q_KEY_CODE_VOLUMEUP,
+ [SDL_SCANCODE_VOLUMEDOWN] = Q_KEY_CODE_VOLUMEDOWN,
+
+ [SDL_SCANCODE_KP_COMMA] = Q_KEY_CODE_KP_COMMA,
+ [SDL_SCANCODE_KP_EQUALSAS400] = Q_KEY_CODE_KP_EQUALSAS400,
+
+ [SDL_SCANCODE_INTERNATIONAL1] = Q_KEY_CODE_INTERNATIONAL1,
+ [SDL_SCANCODE_INTERNATIONAL2] = Q_KEY_CODE_INTERNATIONAL2,
+ [SDL_SCANCODE_INTERNATIONAL3] = Q_KEY_CODE_INTERNATIONAL3,
+ [SDL_SCANCODE_INTERNATIONAL4] = Q_KEY_CODE_INTERNATIONAL4,
+ [SDL_SCANCODE_INTERNATIONAL5] = Q_KEY_CODE_INTERNATIONAL5,
+ [SDL_SCANCODE_INTERNATIONAL6] = Q_KEY_CODE_INTERNATIONAL6,
+ [SDL_SCANCODE_INTERNATIONAL7] = Q_KEY_CODE_INTERNATIONAL7,
+ [SDL_SCANCODE_INTERNATIONAL8] = Q_KEY_CODE_INTERNATIONAL8,
+ [SDL_SCANCODE_INTERNATIONAL9] = Q_KEY_CODE_INTERNATIONAL9,
+ [SDL_SCANCODE_LANG1] = Q_KEY_CODE_LANG1,
+ [SDL_SCANCODE_LANG2] = Q_KEY_CODE_LANG2,
+ [SDL_SCANCODE_LANG3] = Q_KEY_CODE_LANG3,
+ [SDL_SCANCODE_LANG4] = Q_KEY_CODE_LANG4,
+ [SDL_SCANCODE_LANG5] = Q_KEY_CODE_LANG5,
+ [SDL_SCANCODE_LANG6] = Q_KEY_CODE_LANG6,
+ [SDL_SCANCODE_LANG7] = Q_KEY_CODE_LANG7,
+ [SDL_SCANCODE_LANG8] = Q_KEY_CODE_LANG8,
+ [SDL_SCANCODE_LANG9] = Q_KEY_CODE_LANG9,
+ [SDL_SCANCODE_ALTERASE] = Q_KEY_CODE_ALTERASE,
+#endif
+ [SDL_SCANCODE_SYSREQ] = Q_KEY_CODE_SYSRQ,
+#if 0
+ [SDL_SCANCODE_CANCEL] = Q_KEY_CODE_CANCEL,
+ [SDL_SCANCODE_CLEAR] = Q_KEY_CODE_CLEAR,
+ [SDL_SCANCODE_PRIOR] = Q_KEY_CODE_PRIOR,
+ [SDL_SCANCODE_RETURN2] = Q_KEY_CODE_RETURN2,
+ [SDL_SCANCODE_SEPARATOR] = Q_KEY_CODE_SEPARATOR,
+ [SDL_SCANCODE_OUT] = Q_KEY_CODE_OUT,
+ [SDL_SCANCODE_OPER] = Q_KEY_CODE_OPER,
+ [SDL_SCANCODE_CLEARAGAIN] = Q_KEY_CODE_CLEARAGAIN,
+ [SDL_SCANCODE_CRSEL] = Q_KEY_CODE_CRSEL,
+ [SDL_SCANCODE_EXSEL] = Q_KEY_CODE_EXSEL,
+ [SDL_SCANCODE_KP_00] = Q_KEY_CODE_KP_00,
+ [SDL_SCANCODE_KP_000] = Q_KEY_CODE_KP_000,
+ [SDL_SCANCODE_THOUSANDSSEPARATOR] = Q_KEY_CODE_THOUSANDSSEPARATOR,
+ [SDL_SCANCODE_DECIMALSEPARATOR] = Q_KEY_CODE_DECIMALSEPARATOR,
+ [SDL_SCANCODE_CURRENCYUNIT] = Q_KEY_CODE_CURRENCYUNIT,
+ [SDL_SCANCODE_CURRENCYSUBUNIT] = Q_KEY_CODE_CURRENCYSUBUNIT,
+ [SDL_SCANCODE_KP_LEFTPAREN] = Q_KEY_CODE_KP_LEFTPAREN,
+ [SDL_SCANCODE_KP_RIGHTPAREN] = Q_KEY_CODE_KP_RIGHTPAREN,
+ [SDL_SCANCODE_KP_LEFTBRACE] = Q_KEY_CODE_KP_LEFTBRACE,
+ [SDL_SCANCODE_KP_RIGHTBRACE] = Q_KEY_CODE_KP_RIGHTBRACE,
+ [SDL_SCANCODE_KP_TAB] = Q_KEY_CODE_KP_TAB,
+ [SDL_SCANCODE_KP_BACKSPACE] = Q_KEY_CODE_KP_BACKSPACE,
+ [SDL_SCANCODE_KP_A] = Q_KEY_CODE_KP_A,
+ [SDL_SCANCODE_KP_B] = Q_KEY_CODE_KP_B,
+ [SDL_SCANCODE_KP_C] = Q_KEY_CODE_KP_C,
+ [SDL_SCANCODE_KP_D] = Q_KEY_CODE_KP_D,
+ [SDL_SCANCODE_KP_E] = Q_KEY_CODE_KP_E,
+ [SDL_SCANCODE_KP_F] = Q_KEY_CODE_KP_F,
+ [SDL_SCANCODE_KP_XOR] = Q_KEY_CODE_KP_XOR,
+ [SDL_SCANCODE_KP_POWER] = Q_KEY_CODE_KP_POWER,
+ [SDL_SCANCODE_KP_PERCENT] = Q_KEY_CODE_KP_PERCENT,
+ [SDL_SCANCODE_KP_LESS] = Q_KEY_CODE_KP_LESS,
+ [SDL_SCANCODE_KP_GREATER] = Q_KEY_CODE_KP_GREATER,
+ [SDL_SCANCODE_KP_AMPERSAND] = Q_KEY_CODE_KP_AMPERSAND,
+ [SDL_SCANCODE_KP_DBLAMPERSAND] = Q_KEY_CODE_KP_DBLAMPERSAND,
+ [SDL_SCANCODE_KP_VERTICALBAR] = Q_KEY_CODE_KP_VERTICALBAR,
+ [SDL_SCANCODE_KP_DBLVERTICALBAR] = Q_KEY_CODE_KP_DBLVERTICALBAR,
+ [SDL_SCANCODE_KP_COLON] = Q_KEY_CODE_KP_COLON,
+ [SDL_SCANCODE_KP_HASH] = Q_KEY_CODE_KP_HASH,
+ [SDL_SCANCODE_KP_SPACE] = Q_KEY_CODE_KP_SPACE,
+ [SDL_SCANCODE_KP_AT] = Q_KEY_CODE_KP_AT,
+ [SDL_SCANCODE_KP_EXCLAM] = Q_KEY_CODE_KP_EXCLAM,
+ [SDL_SCANCODE_KP_MEMSTORE] = Q_KEY_CODE_KP_MEMSTORE,
+ [SDL_SCANCODE_KP_MEMRECALL] = Q_KEY_CODE_KP_MEMRECALL,
+ [SDL_SCANCODE_KP_MEMCLEAR] = Q_KEY_CODE_KP_MEMCLEAR,
+ [SDL_SCANCODE_KP_MEMADD] = Q_KEY_CODE_KP_MEMADD,
+ [SDL_SCANCODE_KP_MEMSUBTRACT] = Q_KEY_CODE_KP_MEMSUBTRACT,
+ [SDL_SCANCODE_KP_MEMMULTIPLY] = Q_KEY_CODE_KP_MEMMULTIPLY,
+ [SDL_SCANCODE_KP_MEMDIVIDE] = Q_KEY_CODE_KP_MEMDIVIDE,
+ [SDL_SCANCODE_KP_PLUSMINUS] = Q_KEY_CODE_KP_PLUSMINUS,
+ [SDL_SCANCODE_KP_CLEAR] = Q_KEY_CODE_KP_CLEAR,
+ [SDL_SCANCODE_KP_CLEARENTRY] = Q_KEY_CODE_KP_CLEARENTRY,
+ [SDL_SCANCODE_KP_BINARY] = Q_KEY_CODE_KP_BINARY,
+ [SDL_SCANCODE_KP_OCTAL] = Q_KEY_CODE_KP_OCTAL,
+ [SDL_SCANCODE_KP_DECIMAL] = Q_KEY_CODE_KP_DECIMAL,
+ [SDL_SCANCODE_KP_HEXADECIMAL] = Q_KEY_CODE_KP_HEXADECIMAL,
+#endif
+ [SDL_SCANCODE_LCTRL] = Q_KEY_CODE_CTRL,
+ [SDL_SCANCODE_LSHIFT] = Q_KEY_CODE_SHIFT,
+ [SDL_SCANCODE_LALT] = Q_KEY_CODE_ALT,
+ [SDL_SCANCODE_LGUI] = Q_KEY_CODE_META_L,
+ [SDL_SCANCODE_RCTRL] = Q_KEY_CODE_CTRL_R,
+ [SDL_SCANCODE_RSHIFT] = Q_KEY_CODE_SHIFT_R,
+ [SDL_SCANCODE_RALT] = Q_KEY_CODE_ALTGR,
+ [SDL_SCANCODE_RGUI] = Q_KEY_CODE_META_R,
+#if 0
+ [SDL_SCANCODE_MODE] = Q_KEY_CODE_MODE,
+ [SDL_SCANCODE_AUDIONEXT] = Q_KEY_CODE_AUDIONEXT,
+ [SDL_SCANCODE_AUDIOPREV] = Q_KEY_CODE_AUDIOPREV,
+ [SDL_SCANCODE_AUDIOSTOP] = Q_KEY_CODE_AUDIOSTOP,
+ [SDL_SCANCODE_AUDIOPLAY] = Q_KEY_CODE_AUDIOPLAY,
+ [SDL_SCANCODE_AUDIOMUTE] = Q_KEY_CODE_AUDIOMUTE,
+ [SDL_SCANCODE_MEDIASELECT] = Q_KEY_CODE_MEDIASELECT,
+ [SDL_SCANCODE_WWW] = Q_KEY_CODE_WWW,
+ [SDL_SCANCODE_MAIL] = Q_KEY_CODE_MAIL,
+ [SDL_SCANCODE_CALCULATOR] = Q_KEY_CODE_CALCULATOR,
+ [SDL_SCANCODE_COMPUTER] = Q_KEY_CODE_COMPUTER,
+ [SDL_SCANCODE_AC_SEARCH] = Q_KEY_CODE_AC_SEARCH,
+ [SDL_SCANCODE_AC_HOME] = Q_KEY_CODE_AC_HOME,
+ [SDL_SCANCODE_AC_BACK] = Q_KEY_CODE_AC_BACK,
+ [SDL_SCANCODE_AC_FORWARD] = Q_KEY_CODE_AC_FORWARD,
+ [SDL_SCANCODE_AC_STOP] = Q_KEY_CODE_AC_STOP,
+ [SDL_SCANCODE_AC_REFRESH] = Q_KEY_CODE_AC_REFRESH,
+ [SDL_SCANCODE_AC_BOOKMARKS] = Q_KEY_CODE_AC_BOOKMARKS,
+ [SDL_SCANCODE_BRIGHTNESSDOWN] = Q_KEY_CODE_BRIGHTNESSDOWN,
+ [SDL_SCANCODE_BRIGHTNESSUP] = Q_KEY_CODE_BRIGHTNESSUP,
+ [SDL_SCANCODE_DISPLAYSWITCH] = Q_KEY_CODE_DISPLAYSWITCH,
+ [SDL_SCANCODE_KBDILLUMTOGGLE] = Q_KEY_CODE_KBDILLUMTOGGLE,
+ [SDL_SCANCODE_KBDILLUMDOWN] = Q_KEY_CODE_KBDILLUMDOWN,
+ [SDL_SCANCODE_KBDILLUMUP] = Q_KEY_CODE_KBDILLUMUP,
+ [SDL_SCANCODE_EJECT] = Q_KEY_CODE_EJECT,
+ [SDL_SCANCODE_SLEEP] = Q_KEY_CODE_SLEEP,
+ [SDL_SCANCODE_APP1] = Q_KEY_CODE_APP1,
+ [SDL_SCANCODE_APP2] = Q_KEY_CODE_APP2,
+#endif
+};
diff --git a/ui/sdl2.c b/ui/sdl2.c
new file mode 100644
index 000000000..7506e2e13
--- /dev/null
+++ b/ui/sdl2.c
@@ -0,0 +1,834 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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.
+ */
+/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+
+/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
+#undef WIN32_LEAN_AND_MEAN
+
+#include <SDL.h>
+
+#if SDL_MAJOR_VERSION == 2
+#include <SDL_syswm.h>
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "sysemu/sysemu.h"
+#include "sdl_zoom.h"
+
+#include "sdl2-keymap.h"
+
+static int sdl2_num_outputs;
+static struct sdl2_state {
+ DisplayChangeListener dcl;
+ DisplaySurface *surface;
+ SDL_Texture *texture;
+ SDL_Window *real_window;
+ SDL_Renderer *real_renderer;
+ int idx;
+ int last_vm_running; /* per console for caption reasons */
+ int x, y;
+} *sdl2_console;
+
+static SDL_Surface *guest_sprite_surface;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+
+static bool gui_saved_scaling;
+static int gui_saved_width;
+static int gui_saved_height;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_noframe;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[SDL_NUM_SCANCODES];
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled;
+static int guest_cursor;
+static int guest_x, guest_y;
+static SDL_Cursor *guest_sprite;
+static int scaling_active;
+static Notifier mouse_mode_notifier;
+
+static void sdl_update_caption(struct sdl2_state *scon);
+
+static struct sdl2_state *get_scon_from_window(uint32_t window_id)
+{
+ int i;
+ for (i = 0; i < sdl2_num_outputs; i++) {
+ if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
+ return &sdl2_console[i];
+ }
+ }
+ return NULL;
+}
+
+static void sdl_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+ SDL_Rect rect;
+ DisplaySurface *surf = qemu_console_surface(dcl->con);
+
+ if (!surf) {
+ return;
+ }
+ if (!scon->texture) {
+ return;
+ }
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
+ surface_stride(surf));
+ SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
+ SDL_RenderPresent(scon->real_renderer);
+}
+
+static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
+ int bpp)
+{
+ int flags;
+
+ if (scon->real_window && scon->real_renderer) {
+ if (width && height) {
+ SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
+ SDL_SetWindowSize(scon->real_window, width, height);
+ } else {
+ SDL_DestroyRenderer(scon->real_renderer);
+ SDL_DestroyWindow(scon->real_window);
+ scon->real_renderer = NULL;
+ scon->real_window = NULL;
+ }
+ } else {
+ if (!width || !height) {
+ return;
+ }
+ flags = 0;
+ if (gui_fullscreen) {
+ flags |= SDL_WINDOW_FULLSCREEN;
+ } else {
+ flags |= SDL_WINDOW_RESIZABLE;
+ }
+
+ scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ width, height, flags);
+ scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
+ sdl_update_caption(scon);
+ }
+}
+
+static void sdl_switch(DisplayChangeListener *dcl,
+ DisplaySurface *new_surface)
+{
+ struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+ int format = 0;
+ int idx = scon->idx;
+ DisplaySurface *old_surface = scon->surface;
+
+ /* temporary hack: allows to call sdl_switch to handle scaling changes */
+ if (new_surface) {
+ scon->surface = new_surface;
+ }
+
+ if (!new_surface && idx > 0) {
+ scon->surface = NULL;
+ }
+
+ if (new_surface == NULL) {
+ do_sdl_resize(scon, 0, 0, 0);
+ } else {
+ do_sdl_resize(scon, surface_width(scon->surface),
+ surface_height(scon->surface), 0);
+ }
+
+ if (old_surface && scon->texture) {
+ SDL_DestroyTexture(scon->texture);
+ scon->texture = NULL;
+ }
+
+ if (new_surface) {
+ if (!scon->texture) {
+ if (surface_bits_per_pixel(scon->surface) == 16) {
+ format = SDL_PIXELFORMAT_RGB565;
+ } else if (surface_bits_per_pixel(scon->surface) == 32) {
+ format = SDL_PIXELFORMAT_ARGB8888;
+ }
+
+ scon->texture = SDL_CreateTexture(scon->real_renderer, format,
+ SDL_TEXTUREACCESS_STREAMING,
+ surface_width(new_surface),
+ surface_height(new_surface));
+ }
+ }
+}
+
+static void reset_keys(void)
+{
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ if (modifiers_state[i]) {
+ int qcode = sdl2_scancode_to_qcode[i];
+ qemu_input_event_send_key_qcode(NULL, qcode, false);
+ modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+ int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
+
+ switch (ev->keysym.scancode) {
+#if 0
+ case SDL_SCANCODE_NUMLOCKCLEAR:
+ case SDL_SCANCODE_CAPSLOCK:
+ /* SDL does not send the key up event, so we generate it */
+ qemu_input_event_send_key_qcode(NULL, qcode, true);
+ qemu_input_event_send_key_qcode(NULL, qcode, false);
+ return;
+#endif
+ case SDL_SCANCODE_LCTRL:
+ case SDL_SCANCODE_LSHIFT:
+ case SDL_SCANCODE_LALT:
+ case SDL_SCANCODE_LGUI:
+ case SDL_SCANCODE_RCTRL:
+ case SDL_SCANCODE_RSHIFT:
+ case SDL_SCANCODE_RALT:
+ case SDL_SCANCODE_RGUI:
+ if (ev->type == SDL_KEYUP) {
+ modifiers_state[ev->keysym.scancode] = 0;
+ } else {
+ modifiers_state[ev->keysym.scancode] = 1;
+ }
+ /* fall though */
+ default:
+ qemu_input_event_send_key_qcode(NULL, qcode,
+ ev->type == SDL_KEYDOWN);
+ }
+}
+
+static void sdl_update_caption(struct sdl2_state *scon)
+{
+ char win_title[1024];
+ char icon_title[1024];
+ const char *status = "";
+
+ if (!runstate_is_running()) {
+ status = " [Stopped]";
+ } else if (gui_grab) {
+ if (alt_grab) {
+ status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+ } else if (ctrl_grab) {
+ status = " - Press Right-Ctrl to exit mouse grab";
+ } else {
+ status = " - Press Ctrl-Alt to exit mouse grab";
+ }
+ }
+
+ if (qemu_name) {
+ snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
+ scon->idx, status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+ } else {
+ snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU");
+ }
+
+ if (scon->real_window) {
+ SDL_SetWindowTitle(scon->real_window, win_title);
+ }
+}
+
+static void sdl_hide_cursor(void)
+{
+ if (!cursor_hide) {
+ return;
+ }
+
+ if (qemu_input_is_absolute()) {
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+ } else {
+ SDL_SetRelativeMouseMode(SDL_TRUE);
+ }
+}
+
+static void sdl_show_cursor(void)
+{
+ if (!cursor_hide) {
+ return;
+ }
+
+ if (!qemu_input_is_absolute()) {
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+ SDL_ShowCursor(1);
+ if (guest_cursor &&
+ (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
+ SDL_SetCursor(guest_sprite);
+ } else {
+ SDL_SetCursor(sdl_cursor_normal);
+ }
+ }
+}
+
+static void sdl_grab_start(struct sdl2_state *scon)
+{
+ /*
+ * If the application is not active, do not try to enter grab state. This
+ * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
+ * application (SDL bug).
+ */
+ if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
+ return;
+ }
+ if (guest_cursor) {
+ SDL_SetCursor(guest_sprite);
+ if (!qemu_input_is_absolute() && !absolute_enabled) {
+ SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
+ }
+ } else {
+ sdl_hide_cursor();
+ }
+ SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
+ gui_grab = 1;
+ sdl_update_caption(scon);
+}
+
+static void sdl_grab_end(struct sdl2_state *scon)
+{
+ SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
+ gui_grab = 0;
+ sdl_show_cursor();
+ sdl_update_caption(scon);
+}
+
+static void absolute_mouse_grab(struct sdl2_state *scon)
+{
+ int mouse_x, mouse_y;
+ int scr_w, scr_h;
+ SDL_GetMouseState(&mouse_x, &mouse_y);
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+ if (mouse_x > 0 && mouse_x < scr_w - 1 &&
+ mouse_y > 0 && mouse_y < scr_h - 1) {
+ sdl_grab_start(scon);
+ }
+}
+
+static void sdl_mouse_mode_change(Notifier *notify, void *data)
+{
+ if (qemu_input_is_absolute()) {
+ if (!absolute_enabled) {
+ absolute_enabled = 1;
+ absolute_mouse_grab(&sdl2_console[0]);
+ }
+ } else if (absolute_enabled) {
+ if (!gui_fullscreen) {
+ sdl_grab_end(&sdl2_console[0]);
+ }
+ absolute_enabled = 0;
+ }
+}
+
+static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
+ int dz, int x, int y, int state)
+{
+ static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
+ [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
+ [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
+#if 0
+ [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
+ [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
+#endif
+ };
+ static uint32_t prev_state;
+
+ if (prev_state != state) {
+ qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
+ prev_state = state;
+ }
+
+ if (qemu_input_is_absolute()) {
+ int scr_w, scr_h;
+ int max_w = 0, max_h = 0;
+ int off_x = 0, off_y = 0;
+ int cur_off_x = 0, cur_off_y = 0;
+ int i;
+
+ for (i = 0; i < sdl2_num_outputs; i++) {
+ struct sdl2_state *thiscon = &sdl2_console[i];
+ if (thiscon->real_window && thiscon->surface) {
+ SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
+ cur_off_x = thiscon->x;
+ cur_off_y = thiscon->y;
+ if (scr_w + cur_off_x > max_w) {
+ max_w = scr_w + cur_off_x;
+ }
+ if (scr_h + cur_off_y > max_h) {
+ max_h = scr_h + cur_off_y;
+ }
+ if (i == scon->idx) {
+ off_x = cur_off_x;
+ off_y = cur_off_y;
+ }
+ }
+ }
+ qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
+ qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
+ } else {
+ if (guest_cursor) {
+ x -= guest_x;
+ y -= guest_y;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+ qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
+ qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
+ }
+ qemu_input_event_sync();
+}
+
+static void sdl_scale(struct sdl2_state *scon, int width, int height)
+{
+ int bpp = 0;
+ do_sdl_resize(scon, width, height, bpp);
+ scaling_active = 1;
+}
+
+static void toggle_full_screen(struct sdl2_state *scon)
+{
+ int width = surface_width(scon->surface);
+ int height = surface_height(scon->surface);
+ int bpp = surface_bits_per_pixel(scon->surface);
+
+ gui_fullscreen = !gui_fullscreen;
+ if (gui_fullscreen) {
+ SDL_GetWindowSize(scon->real_window,
+ &gui_saved_width, &gui_saved_height);
+ gui_saved_scaling = scaling_active;
+
+ do_sdl_resize(scon, width, height, bpp);
+ scaling_active = 0;
+
+ gui_saved_grab = gui_grab;
+ sdl_grab_start(scon);
+ } else {
+ if (gui_saved_scaling) {
+ sdl_scale(scon, gui_saved_width, gui_saved_height);
+ } else {
+ do_sdl_resize(scon, width, height, 0);
+ }
+ if (!gui_saved_grab) {
+ sdl_grab_end(scon);
+ }
+ }
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
+}
+
+static void handle_keydown(SDL_Event *ev)
+{
+ int mod_state;
+ struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+ if (alt_grab) {
+ mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
+ (gui_grab_code | KMOD_LSHIFT);
+ } else if (ctrl_grab) {
+ mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+ } else {
+ mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
+ }
+ gui_key_modifier_pressed = mod_state;
+
+ if (gui_key_modifier_pressed) {
+ switch (ev->key.keysym.scancode) {
+ case SDL_SCANCODE_F:
+ toggle_full_screen(scon);
+ gui_keysym = 1;
+ break;
+ case SDL_SCANCODE_U:
+ if (scaling_active) {
+ scaling_active = 0;
+ sdl_switch(&scon->dcl, NULL);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
+ }
+ gui_keysym = 1;
+ break;
+ case SDL_SCANCODE_KP_PLUS:
+ case SDL_SCANCODE_KP_MINUS:
+ if (!gui_fullscreen) {
+ int scr_w, scr_h;
+ int width, height;
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+
+ width = MAX(scr_w + (ev->key.keysym.scancode ==
+ SDL_SCANCODE_KP_PLUS ? 50 : -50),
+ 160);
+ height = (surface_height(scon->surface) * width) /
+ surface_width(scon->surface);
+
+ sdl_scale(scon, width, height);
+ graphic_hw_invalidate(NULL);
+ graphic_hw_update(NULL);
+ gui_keysym = 1;
+ }
+ default:
+ break;
+ }
+ }
+ if (!gui_keysym) {
+ sdl_process_key(&ev->key);
+ }
+}
+
+static void handle_keyup(SDL_Event *ev)
+{
+ int mod_state;
+ struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+ if (!alt_grab) {
+ mod_state = (ev->key.keysym.mod & gui_grab_code);
+ } else {
+ mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
+ }
+ if (!mod_state && gui_key_modifier_pressed) {
+ gui_key_modifier_pressed = 0;
+ if (gui_keysym == 0) {
+ /* exit/enter grab if pressing Ctrl-Alt */
+ if (!gui_grab) {
+ sdl_grab_start(scon);
+ } else if (!gui_fullscreen) {
+ sdl_grab_end(scon);
+ }
+ /* SDL does not send back all the modifiers key, so we must
+ * correct it. */
+ reset_keys();
+ return;
+ }
+ gui_keysym = 0;
+ }
+ if (!gui_keysym) {
+ sdl_process_key(&ev->key);
+ }
+}
+
+static void handle_mousemotion(SDL_Event *ev)
+{
+ int max_x, max_y;
+ struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+ if (qemu_input_is_absolute() || absolute_enabled) {
+ int scr_w, scr_h;
+ SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
+ max_x = scr_w - 1;
+ max_y = scr_h - 1;
+ if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
+ ev->motion.x == max_x || ev->motion.y == max_y)) {
+ sdl_grab_end(scon);
+ }
+ if (!gui_grab &&
+ (ev->motion.x > 0 && ev->motion.x < max_x &&
+ ev->motion.y > 0 && ev->motion.y < max_y)) {
+ sdl_grab_start(scon);
+ }
+ }
+ if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+ sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0,
+ ev->motion.x, ev->motion.y, ev->motion.state);
+ }
+}
+
+static void handle_mousebutton(SDL_Event *ev)
+{
+ int buttonstate = SDL_GetMouseState(NULL, NULL);
+ SDL_MouseButtonEvent *bev;
+ struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+ int dz;
+
+ bev = &ev->button;
+ if (!gui_grab && !qemu_input_is_absolute()) {
+ if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
+ /* start grabbing all events */
+ sdl_grab_start(scon);
+ }
+ } else {
+ dz = 0;
+ if (ev->type == SDL_MOUSEBUTTONDOWN) {
+ buttonstate |= SDL_BUTTON(bev->button);
+ } else {
+ buttonstate &= ~SDL_BUTTON(bev->button);
+ }
+#ifdef SDL_BUTTON_WHEELUP
+ if (bev->button == SDL_BUTTON_WHEELUP &&
+ ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = -1;
+ } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
+ ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = 1;
+ }
+#endif
+ sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate);
+ }
+}
+
+static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
+{
+ int w, h;
+ struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
+
+ switch (ev->window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ sdl_scale(scon, ev->window.data1, ev->window.data2);
+ graphic_hw_invalidate(scon->dcl.con);
+ graphic_hw_update(scon->dcl.con);
+ break;
+ case SDL_WINDOWEVENT_EXPOSED:
+ SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
+ sdl_update(dcl, 0, 0, w, h);
+ break;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ case SDL_WINDOWEVENT_ENTER:
+ if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
+ absolute_mouse_grab(scon);
+ }
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ if (gui_grab && !gui_fullscreen) {
+ sdl_grab_end(scon);
+ }
+ break;
+ case SDL_WINDOWEVENT_RESTORED:
+ update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
+ break;
+ case SDL_WINDOWEVENT_MINIMIZED:
+ update_displaychangelistener(dcl, 500);
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ if (!no_quit) {
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+ }
+ break;
+ }
+}
+
+static void sdl_refresh(DisplayChangeListener *dcl)
+{
+ struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+ SDL_Event ev1, *ev = &ev1;
+
+ if (scon->last_vm_running != runstate_is_running()) {
+ scon->last_vm_running = runstate_is_running();
+ sdl_update_caption(scon);
+ }
+
+ graphic_hw_update(dcl->con);
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_KEYDOWN:
+ handle_keydown(ev);
+ break;
+ case SDL_KEYUP:
+ handle_keyup(ev);
+ break;
+ case SDL_QUIT:
+ if (!no_quit) {
+ no_shutdown = 0;
+ qemu_system_shutdown_request();
+ }
+ break;
+ case SDL_MOUSEMOTION:
+ handle_mousemotion(ev);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ handle_mousebutton(ev);
+ break;
+ case SDL_WINDOWEVENT:
+ handle_windowevent(dcl, ev);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void sdl_mouse_warp(DisplayChangeListener *dcl,
+ int x, int y, int on)
+{
+ struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
+ if (on) {
+ if (!guest_cursor) {
+ sdl_show_cursor();
+ }
+ if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
+ SDL_SetCursor(guest_sprite);
+ if (!qemu_input_is_absolute() && !absolute_enabled) {
+ SDL_WarpMouseInWindow(scon->real_window, x, y);
+ }
+ }
+ } else if (gui_grab) {
+ sdl_hide_cursor();
+ }
+ guest_cursor = on;
+ guest_x = x, guest_y = y;
+}
+
+static void sdl_mouse_define(DisplayChangeListener *dcl,
+ QEMUCursor *c)
+{
+
+ if (guest_sprite) {
+ SDL_FreeCursor(guest_sprite);
+ }
+
+ if (guest_sprite_surface) {
+ SDL_FreeSurface(guest_sprite_surface);
+ }
+
+ guest_sprite_surface =
+ SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
+ 0xff0000, 0x00ff00, 0xff, 0xff000000);
+
+ if (!guest_sprite_surface) {
+ fprintf(stderr, "Failed to make rgb surface from %p\n", c);
+ return;
+ }
+ guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
+ c->hot_x, c->hot_y);
+ if (!guest_sprite) {
+ fprintf(stderr, "Failed to make color cursor from %p\n", c);
+ return;
+ }
+ if (guest_cursor &&
+ (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
+ SDL_SetCursor(guest_sprite);
+ }
+}
+
+static void sdl_cleanup(void)
+{
+ if (guest_sprite) {
+ SDL_FreeCursor(guest_sprite);
+ }
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+static const DisplayChangeListenerOps dcl_ops = {
+ .dpy_name = "sdl",
+ .dpy_gfx_update = sdl_update,
+ .dpy_gfx_switch = sdl_switch,
+ .dpy_refresh = sdl_refresh,
+ .dpy_mouse_set = sdl_mouse_warp,
+ .dpy_cursor_define = sdl_mouse_define,
+};
+
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
+{
+ int flags;
+ uint8_t data = 0;
+ char *filename;
+ int i;
+
+ if (no_frame) {
+ gui_noframe = 1;
+ }
+
+#ifdef __linux__
+ /* on Linux, SDL may use fbcon|directfb|svgalib when run without
+ * accessible $DISPLAY to open X11 window. This is often the case
+ * when qemu is run using sudo. But in this case, and when actually
+ * run in X11 environment, SDL fights with X11 for the video card,
+ * making current display unavailable, often until reboot.
+ * So make x11 the default SDL video driver if this variable is unset.
+ * This is a bit hackish but saves us from bigger problem.
+ * Maybe it's a good idea to fix this in SDL instead.
+ */
+ setenv("SDL_VIDEODRIVER", "x11", 0);
+#endif
+
+ flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+ if (SDL_Init(flags)) {
+ fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+ SDL_GetError());
+ exit(1);
+ }
+
+ for (i = 0;; i++) {
+ QemuConsole *con = qemu_console_lookup_by_index(i);
+ if (!con || !qemu_console_is_graphic(con)) {
+ break;
+ }
+ }
+ sdl2_num_outputs = i;
+ sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
+ for (i = 0; i < sdl2_num_outputs; i++) {
+ QemuConsole *con = qemu_console_lookup_by_index(i);
+ sdl2_console[i].dcl.ops = &dcl_ops;
+ sdl2_console[i].dcl.con = con;
+ register_displaychangelistener(&sdl2_console[i].dcl);
+ sdl2_console[i].idx = i;
+ }
+
+ /* Load a 32x32x4 image. White pixels are transparent. */
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
+ if (filename) {
+ SDL_Surface *image = SDL_LoadBMP(filename);
+ if (image) {
+ uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
+ SDL_SetColorKey(image, SDL_TRUE, colorkey);
+ SDL_SetWindowIcon(sdl2_console[0].real_window, image);
+ }
+ g_free(filename);
+ }
+
+ if (full_screen) {
+ gui_fullscreen = 1;
+ sdl_grab_start(0);
+ }
+
+ mouse_mode_notifier.notify = sdl_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
+
+ gui_grab = 0;
+
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ sdl_cursor_normal = SDL_GetCursor();
+
+ atexit(sdl_cleanup);
+}
+#endif
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
index ee904805d..599d9fc64 100644
--- a/ui/sdl_keysym.h
+++ b/ui/sdl_keysym.h
@@ -200,6 +200,7 @@ static const name2keysym_t name2keysym[]={
{ "yacute", 0x0fd},
{ "thorn", 0x0fe},
{ "ydiaeresis", 0x0ff},
+#if SDL_MAJOR_VERSION == 1
{"EuroSign", SDLK_EURO},
/* modifiers */
@@ -272,6 +273,6 @@ static const name2keysym_t name2keysym[]={
{"Num_Lock", SDLK_NUMLOCK},
{"Pause", SDLK_PAUSE},
{"Escape", SDLK_ESCAPE},
-
+#endif
{NULL, 0},
};
diff --git a/ui/spice-core.c b/ui/spice-core.c
index bd7a248f9..4cce3b38c 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -47,8 +47,8 @@ static const char *auth = "spice";
static char *auth_passwd;
static time_t auth_expires = TIME_MAX;
static int spice_migration_completed;
+static int spice_display_is_running;
int using_spice = 0;
-int spice_displays;
static QemuThread me;
@@ -63,25 +63,25 @@ static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
SpiceTimer *timer;
timer = g_malloc0(sizeof(*timer));
- timer->timer = qemu_new_timer_ms(rt_clock, func, opaque);
+ timer->timer = timer_new_ms(QEMU_CLOCK_REALTIME, func, opaque);
QTAILQ_INSERT_TAIL(&timers, timer, next);
return timer;
}
static void timer_start(SpiceTimer *timer, uint32_t ms)
{
- qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms);
+ timer_mod(timer->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + ms);
}
static void timer_cancel(SpiceTimer *timer)
{
- qemu_del_timer(timer->timer);
+ timer_del(timer->timer);
}
static void timer_remove(SpiceTimer *timer)
{
- qemu_del_timer(timer->timer);
- qemu_free_timer(timer->timer);
+ timer_del(timer->timer);
+ timer_free(timer->timer);
QTAILQ_REMOVE(&timers, timer, next);
g_free(timer);
}
@@ -383,17 +383,16 @@ static SpiceChannelList *qmp_query_spice_channels(void)
struct sockaddr *paddr;
socklen_t plen;
+ if (!(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT)) {
+ error_report("invalid channel event");
+ return NULL;
+ }
+
chan = g_malloc0(sizeof(*chan));
chan->value = g_malloc0(sizeof(*chan->value));
- if (item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
- paddr = (struct sockaddr *)&item->info->paddr_ext;
- plen = item->info->plen_ext;
- } else {
- paddr = &item->info->paddr;
- plen = item->info->plen;
- }
-
+ paddr = (struct sockaddr *)&item->info->paddr_ext;
+ plen = item->info->plen_ext;
getnameinfo(paddr, plen,
host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
@@ -511,7 +510,9 @@ SpiceInfo *qmp_query_spice(Error **errp)
int port, tls_port;
const char *addr;
SpiceInfo *info;
- char version_string[20]; /* 12 = |255.255.255\0| is the max */
+ unsigned int major;
+ unsigned int minor;
+ unsigned int micro;
info = g_malloc0(sizeof(*info));
@@ -534,11 +535,10 @@ SpiceInfo *qmp_query_spice(Error **errp)
info->host = g_strdup(addr ? addr : "0.0.0.0");
info->has_compiled_version = true;
- snprintf(version_string, sizeof(version_string), "%d.%d.%d",
- (SPICE_SERVER_VERSION & 0xff0000) >> 16,
- (SPICE_SERVER_VERSION & 0xff00) >> 8,
- SPICE_SERVER_VERSION & 0xff);
- info->compiled_version = g_strdup(version_string);
+ major = (SPICE_SERVER_VERSION & 0xff0000) >> 16;
+ minor = (SPICE_SERVER_VERSION & 0xff00) >> 8;
+ micro = SPICE_SERVER_VERSION & 0xff;
+ info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro);
if (port) {
info->has_port = true;
@@ -623,9 +623,7 @@ static void vm_change_state_handler(void *opaque, int running,
{
if (running) {
qemu_spice_display_start();
- spice_server_vm_start(spice_server);
} else {
- spice_server_vm_stop(spice_server);
qemu_spice_display_stop();
}
}
@@ -640,7 +638,7 @@ void qemu_spice_init(void)
char *x509_key_file = NULL,
*x509_cert_file = NULL,
*x509_cacert_file = NULL;
- int port, tls_port, len, addr_flags;
+ int port, tls_port, addr_flags;
spice_image_compression_t compression;
spice_wan_compression_t wan_compr;
bool seamless_migration;
@@ -671,30 +669,29 @@ void qemu_spice_init(void)
if (NULL == x509_dir) {
x509_dir = ".";
}
- len = strlen(x509_dir) + 32;
str = qemu_opt_get(opts, "x509-key-file");
if (str) {
x509_key_file = g_strdup(str);
} else {
- x509_key_file = g_malloc(len);
- snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
+ x509_key_file = g_strdup_printf("%s/%s", x509_dir,
+ X509_SERVER_KEY_FILE);
}
str = qemu_opt_get(opts, "x509-cert-file");
if (str) {
x509_cert_file = g_strdup(str);
} else {
- x509_cert_file = g_malloc(len);
- snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
+ x509_cert_file = g_strdup_printf("%s/%s", x509_dir,
+ X509_SERVER_CERT_FILE);
}
str = qemu_opt_get(opts, "x509-cacert-file");
if (str) {
x509_cacert_file = g_strdup(str);
} else {
- x509_cacert_file = g_malloc(len);
- snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
+ x509_cacert_file = g_strdup_printf("%s/%s", x509_dir,
+ X509_CA_CERT_FILE);
}
x509_key_password = qemu_opt_get(opts, "x509-key-password");
@@ -778,6 +775,8 @@ void qemu_spice_init(void)
if (str) {
int streaming_video = parse_stream_video(str);
spice_server_set_streaming_video(spice_server, streaming_video);
+ } else {
+ spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
}
spice_server_set_agent_mouse
@@ -833,15 +832,33 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin)
* With a command line like '-vnc :0 -vga qxl' you'll end up here.
*/
spice_server = spice_server_new();
+ spice_server_set_sasl_appname(spice_server, "qemu");
spice_server_init(spice_server, &core_interface);
qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
}
- if (strcmp(sin->sif->type, SPICE_INTERFACE_QXL) == 0) {
- spice_displays++;
+ return spice_server_add_interface(spice_server, sin);
+}
+
+static GSList *spice_consoles;
+static int display_id;
+
+bool qemu_spice_have_display_interface(QemuConsole *con)
+{
+ if (g_slist_find(spice_consoles, con)) {
+ return true;
}
+ return false;
+}
- return spice_server_add_interface(spice_server, sin);
+int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con)
+{
+ if (g_slist_find(spice_consoles, con)) {
+ return -1;
+ }
+ qxlin->id = display_id++;
+ spice_consoles = g_slist_append(spice_consoles, con);
+ return qemu_spice_add_interface(&qxlin->base);
}
static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
@@ -886,6 +903,23 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls)
}
}
+void qemu_spice_display_start(void)
+{
+ spice_display_is_running = true;
+ spice_server_vm_start(spice_server);
+}
+
+void qemu_spice_display_stop(void)
+{
+ spice_server_vm_stop(spice_server);
+ spice_display_is_running = false;
+}
+
+int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
+{
+ return spice_display_is_running;
+}
+
static void spice_register_config(void)
{
qemu_add_opts(&qemu_spice_opts);
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 82d8b9f9a..ce6b220f5 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -83,14 +83,14 @@ void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot,
(uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
QXL_IO_MEMSLOT_ADD_ASYNC));
} else {
- ssd->worker->add_memslot(ssd->worker, memslot);
+ spice_qxl_add_memslot(&ssd->qxl, memslot);
}
}
void qemu_spice_del_memslot(SimpleSpiceDisplay *ssd, uint32_t gid, uint32_t sid)
{
trace_qemu_spice_del_memslot(ssd->qxl.id, gid, sid);
- ssd->worker->del_memslot(ssd->worker, gid, sid);
+ spice_qxl_del_memslot(&ssd->qxl, gid, sid);
}
void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
@@ -103,7 +103,7 @@ void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
(uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
QXL_IO_CREATE_PRIMARY_ASYNC));
} else {
- ssd->worker->create_primary_surface(ssd->worker, id, surface);
+ spice_qxl_create_primary_surface(&ssd->qxl, id, surface);
}
}
@@ -116,31 +116,14 @@ void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
(uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
QXL_IO_DESTROY_PRIMARY_ASYNC));
} else {
- ssd->worker->destroy_primary_surface(ssd->worker, id);
+ spice_qxl_destroy_primary_surface(&ssd->qxl, id);
}
}
void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
{
trace_qemu_spice_wakeup(ssd->qxl.id);
- ssd->worker->wakeup(ssd->worker);
-}
-
-static int spice_display_is_running;
-
-void qemu_spice_display_start(void)
-{
- spice_display_is_running = true;
-}
-
-void qemu_spice_display_stop(void)
-{
- spice_display_is_running = false;
-}
-
-int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
-{
- return spice_display_is_running;
+ spice_qxl_wakeup(&ssd->qxl);
}
static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
@@ -297,7 +280,7 @@ void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd)
{
QXLDevMemSlot memslot;
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
memset(&memslot, 0, sizeof(memslot));
memslot.slot_group_id = MEMSLOT_GROUP_HOST;
@@ -311,7 +294,7 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
memset(&surface, 0, sizeof(surface));
- dprint(1, "%s: %dx%d\n", __FUNCTION__,
+ dprint(1, "%s/%d: %dx%d\n", __func__, ssd->qxl.id,
surface_width(ssd->ds), surface_height(ssd->ds));
surface.format = SPICE_SURFACE_FMT_32_xRGB;
@@ -329,7 +312,7 @@ void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd)
void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
{
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
qemu_spice_destroy_primary_surface(ssd, 0, QXL_SYNC);
}
@@ -354,7 +337,8 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd,
{
QXLRect update_area;
- dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h);
+ dprint(2, "%s/%d: x %d y %d w %d h %d\n", __func__,
+ ssd->qxl.id, x, y, w, h);
update_area.left = x,
update_area.right = x + w;
update_area.top = y;
@@ -370,8 +354,9 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
DisplaySurface *surface)
{
SimpleSpiceUpdate *update;
+ bool need_destroy;
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
if (ssd->surface) {
@@ -382,14 +367,19 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
}
qemu_mutex_lock(&ssd->lock);
+ need_destroy = (ssd->ds != NULL);
ssd->ds = surface;
while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
QTAILQ_REMOVE(&ssd->updates, update, next);
qemu_spice_destroy_update(ssd, update);
}
qemu_mutex_unlock(&ssd->lock);
- qemu_spice_destroy_host_primary(ssd);
- qemu_spice_create_host_primary(ssd);
+ if (need_destroy) {
+ qemu_spice_destroy_host_primary(ssd);
+ }
+ if (ssd->ds) {
+ qemu_spice_create_host_primary(ssd);
+ }
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
ssd->notify++;
@@ -413,7 +403,7 @@ void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
{
- dprint(3, "%s:\n", __func__);
+ dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
graphic_hw_update(ssd->dcl.con);
qemu_mutex_lock(&ssd->lock);
@@ -427,7 +417,7 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
if (ssd->notify) {
ssd->notify = 0;
qemu_spice_wakeup(ssd);
- dprint(2, "%s: notify\n", __FUNCTION__);
+ dprint(2, "%s/%d: notify\n", __func__, ssd->qxl.id);
}
}
@@ -437,19 +427,19 @@ static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
ssd->worker = qxl_worker;
}
static void interface_set_compression_level(QXLInstance *sin, int level)
{
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, sin->id);
/* nothing to do */
}
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
- dprint(3, "%s:\n", __FUNCTION__);
+ dprint(3, "%s/%d:\n", __func__, sin->id);
/* nothing to do */
}
@@ -472,7 +462,7 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
SimpleSpiceUpdate *update;
int ret = false;
- dprint(3, "%s:\n", __FUNCTION__);
+ dprint(3, "%s/%d:\n", __func__, ssd->qxl.id);
qemu_mutex_lock(&ssd->lock);
update = QTAILQ_FIRST(&ssd->updates);
@@ -488,7 +478,7 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
static int interface_req_cmd_notification(QXLInstance *sin)
{
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(1, "%s/%d:\n", __func__, sin->id);
return 1;
}
@@ -498,7 +488,7 @@ static void interface_release_resource(QXLInstance *sin,
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
uintptr_t id;
- dprint(2, "%s:\n", __FUNCTION__);
+ dprint(2, "%s/%d:\n", __func__, ssd->qxl.id);
id = ext.info->id;
qemu_spice_destroy_update(ssd, (void*)id);
}
@@ -553,10 +543,33 @@ static void interface_set_client_capabilities(QXLInstance *sin,
}
static int interface_client_monitors_config(QXLInstance *sin,
- VDAgentMonitorsConfig *monitors_config)
+ VDAgentMonitorsConfig *mc)
{
- dprint(3, "%s:\n", __func__);
- return 0; /* == not supported by guest */
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ QemuUIInfo info;
+ int rc;
+
+ if (!mc) {
+ return 1;
+ }
+
+ /*
+ * FIXME: multihead is tricky due to the way
+ * spice has multihead implemented.
+ */
+ memset(&info, 0, sizeof(info));
+ if (mc->num_of_monitors > 0) {
+ info.width = mc->monitors[0].width;
+ info.height = mc->monitors[0].height;
+ }
+ rc = dpy_set_ui_info(ssd->dcl.con, &info);
+ dprint(1, "%s/%d: size %dx%d, rc %d <--- ==========================\n",
+ __func__, ssd->qxl.id, info.width, info.height, rc);
+ if (rc != 0) {
+ return 0; /* == not supported by guest */
+ } else {
+ return 1;
+ }
}
static const QXLInterface dpy_interface = {
@@ -611,21 +624,36 @@ static const DisplayChangeListenerOps display_listener_ops = {
.dpy_refresh = display_refresh,
};
-void qemu_spice_display_init(DisplayState *ds)
+static void qemu_spice_display_init_one(QemuConsole *con)
{
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
qemu_spice_display_init_common(ssd);
ssd->qxl.base.sif = &dpy_interface.base;
- qemu_spice_add_interface(&ssd->qxl.base);
+ qemu_spice_add_display_interface(&ssd->qxl, con);
assert(ssd->worker);
qemu_spice_create_host_memslot(ssd);
ssd->dcl.ops = &display_listener_ops;
- ssd->dcl.con = qemu_console_lookup_by_index(0);
+ ssd->dcl.con = con;
register_displaychangelistener(&ssd->dcl);
+}
+
+void qemu_spice_display_init(void)
+{
+ QemuConsole *con;
+ int i;
- qemu_spice_create_host_primary(ssd);
+ for (i = 0;; i++) {
+ con = qemu_console_lookup_by_index(i);
+ if (!con || !qemu_console_is_graphic(con)) {
+ break;
+ }
+ if (qemu_spice_have_display_interface(con)) {
+ continue;
+ }
+ qemu_spice_display_init_one(con);
+ }
}
diff --git a/ui/spice-input.c b/ui/spice-input.c
index 3beb8dead..c342e0dcf 100644
--- a/ui/spice-input.c
+++ b/ui/spice-input.c
@@ -26,12 +26,15 @@
#include "qemu-common.h"
#include "ui/qemu-spice.h"
#include "ui/console.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
/* keyboard bits */
typedef struct QemuSpiceKbd {
SpiceKbdInstance sin;
int ledstate;
+ bool emul0;
} QemuSpiceKbd;
static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
@@ -47,9 +50,24 @@ static const SpiceKbdInterface kbd_interface = {
.get_leds = kbd_get_leds,
};
-static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag)
+static void kbd_push_key(SpiceKbdInstance *sin, uint8_t scancode)
{
- kbd_put_keycode(frag);
+ QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin);
+ int keycode;
+ bool up;
+
+ if (scancode == SCANCODE_EMUL0) {
+ kbd->emul0 = true;
+ return;
+ }
+ keycode = scancode & ~SCANCODE_UP;
+ up = scancode & SCANCODE_UP;
+ if (kbd->emul0) {
+ kbd->emul0 = false;
+ keycode |= SCANCODE_GREY;
+ }
+
+ qemu_input_event_send_key_number(NULL, keycode, !up);
}
static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
@@ -80,41 +98,52 @@ static void kbd_leds(void *opaque, int ledstate)
typedef struct QemuSpicePointer {
SpiceMouseInstance mouse;
SpiceTabletInstance tablet;
- int width, height, x, y;
+ int width, height;
+ uint32_t last_bmask;
Notifier mouse_mode;
bool absolute;
} QemuSpicePointer;
-static int map_buttons(int spice_buttons)
+static void spice_update_buttons(QemuSpicePointer *pointer,
+ int wheel, uint32_t button_mask)
{
- int qemu_buttons = 0;
-
- /*
- * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this
- * isn't what we get passed in via interface callbacks for the
- * middle and right button ...
- */
- if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) {
- qemu_buttons |= MOUSE_EVENT_LBUTTON;
+ static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = 0x01,
+ [INPUT_BUTTON_MIDDLE] = 0x04,
+ [INPUT_BUTTON_RIGHT] = 0x02,
+ [INPUT_BUTTON_WHEEL_UP] = 0x10,
+ [INPUT_BUTTON_WHEEL_DOWN] = 0x20,
+ };
+
+ if (wheel < 0) {
+ button_mask |= 0x10;
}
- if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) {
- qemu_buttons |= MOUSE_EVENT_MBUTTON;
+ if (wheel > 0) {
+ button_mask |= 0x20;
}
- if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) {
- qemu_buttons |= MOUSE_EVENT_RBUTTON;
+
+ if (pointer->last_bmask == button_mask) {
+ return;
}
- return qemu_buttons;
+ qemu_input_update_buttons(NULL, bmap, pointer->last_bmask, button_mask);
+ pointer->last_bmask = button_mask;
}
static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz,
uint32_t buttons_state)
{
- kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state));
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse);
+ spice_update_buttons(pointer, dz, buttons_state);
+ qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx);
+ qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy);
+ qemu_input_event_sync();
}
static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state)
{
- kbd_mouse_event(0, 0, 0, map_buttons(buttons_state));
+ QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, mouse);
+ spice_update_buttons(pointer, 0, buttons_state);
+ qemu_input_event_sync();
}
static const SpiceMouseInterface mouse_interface = {
@@ -145,9 +174,10 @@ static void tablet_position(SpiceTabletInstance* sin, int x, int y,
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
- pointer->x = x * 0x7FFF / (pointer->width - 1);
- pointer->y = y * 0x7FFF / (pointer->height - 1);
- kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+ spice_update_buttons(pointer, 0, buttons_state);
+ qemu_input_queue_abs(NULL, INPUT_AXIS_X, x, pointer->width);
+ qemu_input_queue_abs(NULL, INPUT_AXIS_Y, y, pointer->height);
+ qemu_input_event_sync();
}
@@ -156,7 +186,8 @@ static void tablet_wheel(SpiceTabletInstance* sin, int wheel,
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
- kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state));
+ spice_update_buttons(pointer, wheel, buttons_state);
+ qemu_input_event_sync();
}
static void tablet_buttons(SpiceTabletInstance *sin,
@@ -164,7 +195,8 @@ static void tablet_buttons(SpiceTabletInstance *sin,
{
QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet);
- kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state));
+ spice_update_buttons(pointer, 0, buttons_state);
+ qemu_input_event_sync();
}
static const SpiceTabletInterface tablet_interface = {
@@ -181,7 +213,7 @@ static const SpiceTabletInterface tablet_interface = {
static void mouse_mode_notifier(Notifier *notifier, void *data)
{
QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode);
- bool is_absolute = kbd_mouse_is_absolute();
+ bool is_absolute = qemu_input_is_absolute();
if (pointer->absolute == is_absolute) {
return;
diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
index 8091d689c..3f59da67e 100644
--- a/ui/vnc-auth-sasl.h
+++ b/ui/vnc-auth-sasl.h
@@ -33,6 +33,7 @@ typedef struct VncStateSASL VncStateSASL;
typedef struct VncDisplaySASL VncDisplaySASL;
#include "qemu/acl.h"
+#include "qemu/main-loop.h"
struct VncStateSASL {
sasl_conn_t *conn;
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
index c59b18860..bc7032e69 100644
--- a/ui/vnc-auth-vencrypt.c
+++ b/ui/vnc-auth-vencrypt.c
@@ -25,7 +25,7 @@
*/
#include "vnc.h"
-
+#include "qemu/main-loop.h"
static void start_auth_vencrypt_subauth(VncState *vs)
{
diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c
index e6966aebc..59b59c0c7 100644
--- a/ui/vnc-enc-tight.c
+++ b/ui/vnc-enc-tight.c
@@ -330,7 +330,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h)
} else {
errors = tight_detect_smooth_image16(vs, w, h);
}
- if (quality != -1) {
+ if (quality != (uint8_t)-1) {
return (errors < tight_conf[quality].jpeg_threshold);
}
return (errors < tight_conf[compression].gradient_threshold);
diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h
index 1ff40b1f4..d436d588f 100644
--- a/ui/vnc-enc-zywrle.h
+++ b/ui/vnc-enc-zywrle.h
@@ -305,7 +305,7 @@ static inline void harr(int8_t *px0, int8_t *px1)
|L1H0H1H0|L1H0H1H0|L1H0H1H0|L1H0H1H0| : level 1
In this method, H/L and X0/X1 is always same position.
- This lead us to more speed and less memory.
+ This leads us to more speed and less memory.
Of cause, the result of both method is quite same
because it's only difference that coefficient position.
*/
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index 2d3fce815..68f3d773d 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -252,6 +252,8 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
if (job->vs->csock == -1) {
vnc_unlock_display(job->vs->vd);
+ /* Copy persistent encoding data */
+ vnc_async_encoding_end(job->vs, &vs);
goto disconnected;
}
@@ -278,6 +280,9 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
vnc_async_encoding_end(job->vs, &vs);
qemu_bh_schedule(job->vs->bh);
+ } else {
+ /* Copy persistent encoding data */
+ vnc_async_encoding_end(job->vs, &vs);
}
vnc_unlock_output(job->vs);
@@ -333,7 +338,8 @@ void vnc_start_worker_thread(void)
return ;
q = vnc_queue_init();
- qemu_thread_create(&q->thread, vnc_worker_thread, q, QEMU_THREAD_DETACHED);
+ qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread, q,
+ QEMU_THREAD_DETACHED);
queue = q; /* Set global queue */
}
diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c
index df8931573..e304bafeb 100644
--- a/ui/vnc-ws.c
+++ b/ui/vnc-ws.c
@@ -19,6 +19,7 @@
*/
#include "vnc.h"
+#include "qemu/main-loop.h"
#ifdef CONFIG_VNC_TLS
#include "qemu/sockets.h"
diff --git a/ui/vnc.c b/ui/vnc.c
index 5601cc34e..592577450 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -33,6 +33,7 @@
#include "qapi/qmp/types.h"
#include "qmp-commands.h"
#include "qemu/osdep.h"
+#include "ui/input.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
@@ -416,8 +417,7 @@ out_error:
3) resolutions > 1024
*/
-static int vnc_update_client(VncState *vs, int has_dirty);
-static int vnc_update_client_sync(VncState *vs, int has_dirty);
+static int vnc_update_client(VncState *vs, int has_dirty, bool sync);
static void vnc_disconnect_start(VncState *vs);
static void vnc_colordepth(VncState *vs);
@@ -430,29 +430,25 @@ static int vnc_refresh_server_surface(VncDisplay *vd);
static void vnc_dpy_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
- int i;
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
struct VncSurface *s = &vd->guest;
int width = surface_width(vd->ds);
int height = surface_height(vd->ds);
- h += y;
-
- /* round x down to ensure the loop only spans one 16-pixel block per,
- iteration. otherwise, if (x % 16) != 0, the last iteration may span
- two 16-pixel blocks but we only mark the first as dirty
- */
- w += (x % 16);
- x -= (x % 16);
+ /* this is needed this to ensure we updated all affected
+ * blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
+ w += (x % VNC_DIRTY_PIXELS_PER_BIT);
+ x -= (x % VNC_DIRTY_PIXELS_PER_BIT);
x = MIN(x, width);
y = MIN(y, height);
w = MIN(x + w, width) - x;
- h = MIN(h, height);
+ h = MIN(y + h, height);
- for (; y < h; y++)
- for (i = 0; i < w; i += 16)
- set_bit((x + i) / 16, s->dirty[y]);
+ for (; y < h; y++) {
+ bitmap_set(s->dirty[y], x / VNC_DIRTY_PIXELS_PER_BIT,
+ DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));
+ }
}
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
@@ -570,6 +566,15 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
ptr += x * VNC_SERVER_FB_BYTES;
return ptr;
}
+/* this sets only the visible pixels of a dirty bitmap */
+#define VNC_SET_VISIBLE_PIXELS_DIRTY(bitmap, w, h) {\
+ int y;\
+ memset(bitmap, 0x00, sizeof(bitmap));\
+ for (y = 0; y < h; y++) {\
+ bitmap_set(bitmap[y], 0,\
+ DIV_ROUND_UP(w, VNC_DIRTY_PIXELS_PER_BIT));\
+ } \
+ }
static void vnc_dpy_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
@@ -595,7 +600,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format;
- memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
+ VNC_SET_VISIBLE_PIXELS_DIRTY(vd->guest.dirty,
+ surface_width(vd->ds),
+ surface_height(vd->ds));
QTAILQ_FOREACH(vs, &vd->clients, next) {
vnc_colordepth(vs);
@@ -603,7 +610,9 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
if (vs->vd->cursor) {
vnc_cursor_define(vs);
}
- memset(vs->dirty, 0xFF, sizeof(vs->dirty));
+ VNC_SET_VISIBLE_PIXELS_DIRTY(vs->dirty,
+ surface_width(vd->ds),
+ surface_height(vd->ds));
}
}
@@ -750,7 +759,7 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
vs->force_update = 1;
- vnc_update_client_sync(vs, 1);
+ vnc_update_client(vs, 1, true);
/* vs might be free()ed here */
}
}
@@ -769,11 +778,12 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
y = dst_y + h - 1;
inc = -1;
}
- w_lim = w - (16 - (dst_x % 16));
- if (w_lim < 0)
+ w_lim = w - (VNC_DIRTY_PIXELS_PER_BIT - (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
+ if (w_lim < 0) {
w_lim = w;
- else
- w_lim = w - (w_lim % 16);
+ } else {
+ w_lim = w - (w_lim % VNC_DIRTY_PIXELS_PER_BIT);
+ }
for (i = 0; i < h; i++) {
for (x = 0; x <= w_lim;
x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
@@ -781,10 +791,11 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
if ((s = w - w_lim) == 0)
break;
} else if (!x) {
- s = (16 - (dst_x % 16));
+ s = (VNC_DIRTY_PIXELS_PER_BIT -
+ (dst_x % VNC_DIRTY_PIXELS_PER_BIT));
s = MIN(s, w_lim);
} else {
- s = 16;
+ s = VNC_DIRTY_PIXELS_PER_BIT;
}
cmp_bytes = s * VNC_SERVER_FB_BYTES;
if (memcmp(src_row, dst_row, cmp_bytes) == 0)
@@ -792,7 +803,8 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
memmove(dst_row, src_row, cmp_bytes);
QTAILQ_FOREACH(vs, &vd->clients, next) {
if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
- set_bit(((x + dst_x) / 16), vs->dirty[y]);
+ set_bit(((x + dst_x) / VNC_DIRTY_PIXELS_PER_BIT),
+ vs->dirty[y]);
}
}
}
@@ -861,35 +873,24 @@ static int find_and_clear_dirty_height(struct VncState *vs,
int h;
for (h = 1; h < (height - y); h++) {
- int tmp_x;
if (!test_bit(last_x, vs->dirty[y + h])) {
break;
}
- for (tmp_x = last_x; tmp_x < x; tmp_x++) {
- clear_bit(tmp_x, vs->dirty[y + h]);
- }
+ bitmap_clear(vs->dirty[y + h], last_x, x - last_x);
}
return h;
}
-static int vnc_update_client_sync(VncState *vs, int has_dirty)
-{
- int ret = vnc_update_client(vs, has_dirty);
- vnc_jobs_join(vs);
- return ret;
-}
-
-static int vnc_update_client(VncState *vs, int has_dirty)
+static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
{
if (vs->need_update && vs->csock != -1) {
VncDisplay *vd = vs->vd;
VncJob *job;
int y;
- int width, height;
+ int height, width;
int n = 0;
-
if (vs->output.offset && !vs->audio_cap && !vs->force_update)
/* kernel send buffers are full -> drop frames to throttle */
return 0;
@@ -905,32 +906,30 @@ static int vnc_update_client(VncState *vs, int has_dirty)
*/
job = vnc_job_new(vs);
- width = MIN(pixman_image_get_width(vd->server), vs->client_width);
height = MIN(pixman_image_get_height(vd->server), vs->client_height);
+ width = MIN(pixman_image_get_width(vd->server), vs->client_width);
- for (y = 0; y < height; y++) {
- int x;
- int last_x = -1;
- for (x = 0; x < width / 16; x++) {
- if (test_and_clear_bit(x, vs->dirty[y])) {
- if (last_x == -1) {
- last_x = x;
- }
- } else {
- if (last_x != -1) {
- int h = find_and_clear_dirty_height(vs, y, last_x, x,
- height);
-
- n += vnc_job_add_rect(job, last_x * 16, y,
- (x - last_x) * 16, h);
- }
- last_x = -1;
- }
+ y = 0;
+ for (;;) {
+ int x, h;
+ unsigned long x2;
+ unsigned long offset = find_next_bit((unsigned long *) &vs->dirty,
+ height * VNC_DIRTY_BPL(vs),
+ y * VNC_DIRTY_BPL(vs));
+ if (offset == height * VNC_DIRTY_BPL(vs)) {
+ /* no more dirty bits */
+ break;
}
- if (last_x != -1) {
- int h = find_and_clear_dirty_height(vs, y, last_x, x, height);
- n += vnc_job_add_rect(job, last_x * 16, y,
- (x - last_x) * 16, h);
+ y = offset / VNC_DIRTY_BPL(vs);
+ x = offset % VNC_DIRTY_BPL(vs);
+ x2 = find_next_zero_bit((unsigned long *) &vs->dirty[y],
+ VNC_DIRTY_BPL(vs), x);
+ bitmap_clear(vs->dirty[y], x, x2 - x);
+ h = find_and_clear_dirty_height(vs, y, x, x2, height);
+ x2 = MIN(x2, width / VNC_DIRTY_PIXELS_PER_BIT);
+ if (x2 > x) {
+ n += vnc_job_add_rect(job, x * VNC_DIRTY_PIXELS_PER_BIT, y,
+ (x2 - x) * VNC_DIRTY_PIXELS_PER_BIT, h);
}
}
@@ -939,8 +938,11 @@ static int vnc_update_client(VncState *vs, int has_dirty)
return n;
}
- if (vs->csock == -1)
+ if (vs->csock == -1) {
vnc_disconnect_finish(vs);
+ } else if (sync) {
+ vnc_jobs_join(vs);
+ }
return 0;
}
@@ -1483,7 +1485,7 @@ static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
static void check_pointer_type_change(Notifier *notifier, void *data)
{
VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
- int absolute = kbd_mouse_is_absolute();
+ int absolute = qemu_input_is_absolute();
if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
vnc_lock_output(vs);
@@ -1502,39 +1504,37 @@ static void check_pointer_type_change(Notifier *notifier, void *data)
static void pointer_event(VncState *vs, int button_mask, int x, int y)
{
- int buttons = 0;
- int dz = 0;
+ static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = 0x01,
+ [INPUT_BUTTON_MIDDLE] = 0x02,
+ [INPUT_BUTTON_RIGHT] = 0x04,
+ [INPUT_BUTTON_WHEEL_UP] = 0x08,
+ [INPUT_BUTTON_WHEEL_DOWN] = 0x10,
+ };
+ QemuConsole *con = vs->vd->dcl.con;
int width = surface_width(vs->vd->ds);
int height = surface_height(vs->vd->ds);
- if (button_mask & 0x01)
- buttons |= MOUSE_EVENT_LBUTTON;
- if (button_mask & 0x02)
- buttons |= MOUSE_EVENT_MBUTTON;
- if (button_mask & 0x04)
- buttons |= MOUSE_EVENT_RBUTTON;
- if (button_mask & 0x08)
- dz = -1;
- if (button_mask & 0x10)
- dz = 1;
+ if (vs->last_bmask != button_mask) {
+ qemu_input_update_buttons(con, bmap, vs->last_bmask, button_mask);
+ vs->last_bmask = button_mask;
+ }
if (vs->absolute) {
- kbd_mouse_event(width > 1 ? x * 0x7FFF / (width - 1) : 0x4000,
- height > 1 ? y * 0x7FFF / (height - 1) : 0x4000,
- dz, buttons);
+ qemu_input_queue_abs(con, INPUT_AXIS_X, x, width);
+ qemu_input_queue_abs(con, INPUT_AXIS_Y, y, height);
} else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
- x -= 0x7FFF;
- y -= 0x7FFF;
-
- kbd_mouse_event(x, y, dz, buttons);
+ qemu_input_queue_rel(con, INPUT_AXIS_X, x - 0x7FFF);
+ qemu_input_queue_rel(con, INPUT_AXIS_Y, y - 0x7FFF);
} else {
- if (vs->last_x != -1)
- kbd_mouse_event(x - vs->last_x,
- y - vs->last_y,
- dz, buttons);
+ if (vs->last_x != -1) {
+ qemu_input_queue_rel(con, INPUT_AXIS_X, x - vs->last_x);
+ qemu_input_queue_rel(con, INPUT_AXIS_Y, y - vs->last_y);
+ }
vs->last_x = x;
vs->last_y = y;
}
+ qemu_input_event_sync();
}
static void reset_keys(VncState *vs)
@@ -1542,9 +1542,7 @@ static void reset_keys(VncState *vs)
int i;
for(i = 0; i < 256; i++) {
if (vs->modifiers_state[i]) {
- if (i & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(i | SCANCODE_UP);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
vs->modifiers_state[i] = 0;
}
}
@@ -1553,12 +1551,8 @@ static void reset_keys(VncState *vs)
static void press_key(VncState *vs, int keysym)
{
int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
- if (keycode & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
- if (keycode & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- kbd_put_keycode(keycode | SCANCODE_UP);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
}
static int current_led_state(VncState *vs)
@@ -1700,12 +1694,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
}
if (qemu_console_is_graphic(NULL)) {
- if (keycode & SCANCODE_GREY)
- kbd_put_keycode(SCANCODE_EMUL0);
- if (down)
- kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
- else
- kbd_put_keycode(keycode | SCANCODE_UP);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
} else {
bool numlock = vs->modifiers_state[0x45];
bool control = (vs->modifiers_state[0x1d] ||
@@ -1826,10 +1815,7 @@ static void vnc_release_modifiers(VncState *vs)
if (!vs->modifiers_state[keycode]) {
continue;
}
- if (keycode & SCANCODE_GREY) {
- kbd_put_keycode(SCANCODE_EMUL0);
- }
- kbd_put_keycode(keycode | SCANCODE_UP);
+ qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
}
}
@@ -1861,7 +1847,7 @@ static void framebuffer_update_request(VncState *vs, int incremental,
int w, int h)
{
int i;
- const size_t width = surface_width(vs->vd->ds) / 16;
+ const size_t width = surface_width(vs->vd->ds) / VNC_DIRTY_PIXELS_PER_BIT;
const size_t height = surface_height(vs->vd->ds);
if (y_position > height) {
@@ -2563,7 +2549,9 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y)
vs->lossy_rect[sty][stx] = 0;
for (j = 0; j < VNC_STAT_RECT; ++j) {
- bitmap_set(vs->dirty[y + j], x / 16, VNC_STAT_RECT / 16);
+ bitmap_set(vs->dirty[y + j],
+ x / VNC_DIRTY_PIXELS_PER_BIT,
+ VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT);
}
has_dirty++;
}
@@ -2667,8 +2655,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
int width = pixman_image_get_width(vd->guest.fb);
int height = pixman_image_get_height(vd->guest.fb);
int y;
- uint8_t *guest_row;
- uint8_t *server_row;
+ uint8_t *guest_row0 = NULL, *server_row0;
+ int guest_stride = 0, server_stride;
int cmp_bytes;
VncState *vs;
int has_dirty = 0;
@@ -2686,47 +2674,64 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
* Check and copy modified bits from guest to server surface.
* Update server dirty map.
*/
- cmp_bytes = 64;
+ cmp_bytes = VNC_DIRTY_PIXELS_PER_BIT * VNC_SERVER_FB_BYTES;
if (cmp_bytes > vnc_server_fb_stride(vd)) {
cmp_bytes = vnc_server_fb_stride(vd);
}
if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
int width = pixman_image_get_width(vd->server);
tmpbuf = qemu_pixman_linebuf_create(VNC_SERVER_FB_FORMAT, width);
- }
- guest_row = (uint8_t *)pixman_image_get_data(vd->guest.fb);
- server_row = (uint8_t *)pixman_image_get_data(vd->server);
- for (y = 0; y < height; y++) {
- if (!bitmap_empty(vd->guest.dirty[y], VNC_DIRTY_BITS)) {
- int x;
- uint8_t *guest_ptr;
- uint8_t *server_ptr;
-
- if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
- qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
- guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
- } else {
- guest_ptr = guest_row;
- }
- server_ptr = server_row;
+ } else {
+ guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb);
+ guest_stride = pixman_image_get_stride(vd->guest.fb);
+ }
+ server_row0 = (uint8_t *)pixman_image_get_data(vd->server);
+ server_stride = pixman_image_get_stride(vd->server);
+
+ y = 0;
+ for (;;) {
+ int x;
+ uint8_t *guest_ptr, *server_ptr;
+ unsigned long offset = find_next_bit((unsigned long *) &vd->guest.dirty,
+ height * VNC_DIRTY_BPL(&vd->guest),
+ y * VNC_DIRTY_BPL(&vd->guest));
+ if (offset == height * VNC_DIRTY_BPL(&vd->guest)) {
+ /* no more dirty bits */
+ break;
+ }
+ y = offset / VNC_DIRTY_BPL(&vd->guest);
+ x = offset % VNC_DIRTY_BPL(&vd->guest);
- for (x = 0; x + 15 < width;
- x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
- if (!test_and_clear_bit((x / 16), vd->guest.dirty[y]))
- continue;
- if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
- continue;
- memcpy(server_ptr, guest_ptr, cmp_bytes);
- if (!vd->non_adaptive)
- vnc_rect_updated(vd, x, y, &tv);
- QTAILQ_FOREACH(vs, &vd->clients, next) {
- set_bit((x / 16), vs->dirty[y]);
- }
- has_dirty++;
+ server_ptr = server_row0 + y * server_stride + x * cmp_bytes;
+
+ if (vd->guest.format != VNC_SERVER_FB_FORMAT) {
+ qemu_pixman_linebuf_fill(tmpbuf, vd->guest.fb, width, 0, y);
+ guest_ptr = (uint8_t *)pixman_image_get_data(tmpbuf);
+ } else {
+ guest_ptr = guest_row0 + y * guest_stride;
+ }
+ guest_ptr += x * cmp_bytes;
+
+ for (; x < DIV_ROUND_UP(width, VNC_DIRTY_PIXELS_PER_BIT);
+ x++, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
+ if (!test_and_clear_bit(x, vd->guest.dirty[y])) {
+ continue;
+ }
+ if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) {
+ continue;
+ }
+ memcpy(server_ptr, guest_ptr, cmp_bytes);
+ if (!vd->non_adaptive) {
+ vnc_rect_updated(vd, x * VNC_DIRTY_PIXELS_PER_BIT,
+ y, &tv);
}
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ set_bit(x, vs->dirty[y]);
+ }
+ has_dirty++;
}
- guest_row += pixman_image_get_stride(vd->guest.fb);
- server_row += pixman_image_get_stride(vd->server);
+
+ y++;
}
qemu_pixman_image_unref(tmpbuf);
return has_dirty;
@@ -2749,7 +2754,7 @@ static void vnc_refresh(DisplayChangeListener *dcl)
vnc_unlock_display(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
- rects += vnc_update_client(vs, has_dirty);
+ rects += vnc_update_client(vs, has_dirty, false);
/* vs might be free()ed here */
}
@@ -3149,7 +3154,9 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
acl = 1;
#endif
} else if (strncmp(options, "lossy", 5) == 0) {
+#ifdef CONFIG_VNC_JPEG
vs->lossy = true;
+#endif
} else if (strncmp(options, "non-adaptive", 12) == 0) {
vs->non_adaptive = true;
} else if (strncmp(options, "share=", 6) == 0) {
@@ -3166,6 +3173,13 @@ void vnc_display_open(DisplayState *ds, const char *display, Error **errp)
}
}
+ /* adaptive updates are only used with tight encoding and
+ * if lossy updates are enabled so we can disable all the
+ * calculations otherwise */
+ if (!vs->lossy) {
+ vs->non_adaptive = true;
+ }
+
#ifdef CONFIG_VNC_TLS
if (acl && x509 && vs->tls.x509verify) {
if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
diff --git a/ui/vnc.h b/ui/vnc.h
index 6e9921387..8da81b8d6 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -81,8 +81,16 @@ typedef void VncSendHextileTile(VncState *vs,
#define VNC_MAX_WIDTH 2560
#define VNC_MAX_HEIGHT 2048
+/* VNC_DIRTY_PIXELS_PER_BIT is the number of dirty pixels represented
+ * by one bit in the dirty bitmap */
+#define VNC_DIRTY_PIXELS_PER_BIT 16
+
/* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */
-#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / 16)
+#define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT)
+
+/* VNC_DIRTY_BPL (BPL = bits per line) might be greater than
+ * VNC_DIRTY_BITS due to alignment */
+#define VNC_DIRTY_BPL(x) (sizeof((x)->dirty) / VNC_MAX_HEIGHT * BITS_PER_BYTE)
#define VNC_STAT_RECT 64
#define VNC_STAT_COLS (VNC_MAX_WIDTH / VNC_STAT_RECT)
@@ -257,6 +265,7 @@ struct VncState
int absolute;
int last_x;
int last_y;
+ uint32_t last_bmask;
int client_width;
int client_height;
VncShareMode share_mode;
diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h
index 6250bec69..1dc039f71 100644
--- a/ui/vnc_keysym.h
+++ b/ui/vnc_keysym.h
@@ -224,6 +224,14 @@ static const name2keysym_t name2keysym[]={
{ "odoubleacute", 0x1f5},
{ "udoubleacute", 0x1fb},
+/* Czech national characters */
+{ "ecaron", 0x1ec},
+{ "scaron", 0x1b9},
+{ "ccaron", 0x1e8},
+{ "rcaron", 0x1f8},
+{ "zcaron", 0x1be},
+{ "uring", 0x1f9},
+
/* modifiers */
{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */
{"Control_L", 0xffe3}, /* XK_Control_L */
@@ -342,5 +350,370 @@ static const name2keysym_t name2keysym[]={
{"Katakana_Real", 0xff25},
{"Eisu_toggle", 0xff30},
+{"abovedot", 0x01ff}, /* U+02D9 DOT ABOVE */
+{"amacron", 0x03e0}, /* U+0101 LATIN SMALL LETTER A WITH MACRON */
+{"Amacron", 0x03c0}, /* U+0100 LATIN CAPITAL LETTER A WITH MACRON */
+{"Arabic_ain", 0x05d9}, /* U+0639 ARABIC LETTER AIN */
+{"Arabic_alef", 0x05c7}, /* U+0627 ARABIC LETTER ALEF */
+{"Arabic_alefmaksura", 0x05e9}, /* U+0649 ARABIC LETTER ALEF MAKSURA */
+{"Arabic_beh", 0x05c8}, /* U+0628 ARABIC LETTER BEH */
+{"Arabic_comma", 0x05ac}, /* U+060C ARABIC COMMA */
+{"Arabic_dad", 0x05d6}, /* U+0636 ARABIC LETTER DAD */
+{"Arabic_dal", 0x05cf}, /* U+062F ARABIC LETTER DAL */
+{"Arabic_damma", 0x05ef}, /* U+064F ARABIC DAMMA */
+{"Arabic_dammatan", 0x05ec}, /* U+064C ARABIC DAMMATAN */
+{"Arabic_fatha", 0x05ee}, /* U+064E ARABIC FATHA */
+{"Arabic_fathatan", 0x05eb}, /* U+064B ARABIC FATHATAN */
+{"Arabic_feh", 0x05e1}, /* U+0641 ARABIC LETTER FEH */
+{"Arabic_ghain", 0x05da}, /* U+063A ARABIC LETTER GHAIN */
+{"Arabic_ha", 0x05e7}, /* U+0647 ARABIC LETTER HEH */
+{"Arabic_hah", 0x05cd}, /* U+062D ARABIC LETTER HAH */
+{"Arabic_hamza", 0x05c1}, /* U+0621 ARABIC LETTER HAMZA */
+{"Arabic_hamzaonalef", 0x05c3}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */
+{"Arabic_hamzaonwaw", 0x05c4}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */
+{"Arabic_hamzaonyeh", 0x05c6}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */
+{"Arabic_hamzaunderalef", 0x05c5}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */
+{"Arabic_jeem", 0x05cc}, /* U+062C ARABIC LETTER JEEM */
+{"Arabic_kaf", 0x05e3}, /* U+0643 ARABIC LETTER KAF */
+{"Arabic_kasra", 0x05f0}, /* U+0650 ARABIC KASRA */
+{"Arabic_kasratan", 0x05ed}, /* U+064D ARABIC KASRATAN */
+{"Arabic_khah", 0x05ce}, /* U+062E ARABIC LETTER KHAH */
+{"Arabic_lam", 0x05e4}, /* U+0644 ARABIC LETTER LAM */
+{"Arabic_maddaonalef", 0x05c2}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */
+{"Arabic_meem", 0x05e5}, /* U+0645 ARABIC LETTER MEEM */
+{"Arabic_noon", 0x05e6}, /* U+0646 ARABIC LETTER NOON */
+{"Arabic_qaf", 0x05e2}, /* U+0642 ARABIC LETTER QAF */
+{"Arabic_question_mark", 0x05bf}, /* U+061F ARABIC QUESTION MARK */
+{"Arabic_ra", 0x05d1}, /* U+0631 ARABIC LETTER REH */
+{"Arabic_sad", 0x05d5}, /* U+0635 ARABIC LETTER SAD */
+{"Arabic_seen", 0x05d3}, /* U+0633 ARABIC LETTER SEEN */
+{"Arabic_semicolon", 0x05bb}, /* U+061B ARABIC SEMICOLON */
+{"Arabic_shadda", 0x05f1}, /* U+0651 ARABIC SHADDA */
+{"Arabic_sheen", 0x05d4}, /* U+0634 ARABIC LETTER SHEEN */
+{"Arabic_sukun", 0x05f2}, /* U+0652 ARABIC SUKUN */
+{"Arabic_tah", 0x05d7}, /* U+0637 ARABIC LETTER TAH */
+{"Arabic_tatweel", 0x05e0}, /* U+0640 ARABIC TATWEEL */
+{"Arabic_teh", 0x05ca}, /* U+062A ARABIC LETTER TEH */
+{"Arabic_tehmarbuta", 0x05c9}, /* U+0629 ARABIC LETTER TEH MARBUTA */
+{"Arabic_thal", 0x05d0}, /* U+0630 ARABIC LETTER THAL */
+{"Arabic_theh", 0x05cb}, /* U+062B ARABIC LETTER THEH */
+{"Arabic_waw", 0x05e8}, /* U+0648 ARABIC LETTER WAW */
+{"Arabic_yeh", 0x05ea}, /* U+064A ARABIC LETTER YEH */
+{"Arabic_zah", 0x05d8}, /* U+0638 ARABIC LETTER ZAH */
+{"Arabic_zain", 0x05d2}, /* U+0632 ARABIC LETTER ZAIN */
+{"breve", 0x01a2}, /* U+02D8 BREVE */
+{"caron", 0x01b7}, /* U+02C7 CARON */
+{"Ccaron", 0x01c8}, /* U+010C LATIN CAPITAL LETTER C WITH CARON */
+{"Cyrillic_a", 0x06c1}, /* U+0430 CYRILLIC SMALL LETTER A */
+{"Cyrillic_A", 0x06e1}, /* U+0410 CYRILLIC CAPITAL LETTER A */
+{"Cyrillic_be", 0x06c2}, /* U+0431 CYRILLIC SMALL LETTER BE */
+{"Cyrillic_BE", 0x06e2}, /* U+0411 CYRILLIC CAPITAL LETTER BE */
+{"Cyrillic_che", 0x06de}, /* U+0447 CYRILLIC SMALL LETTER CHE */
+{"Cyrillic_CHE", 0x06fe}, /* U+0427 CYRILLIC CAPITAL LETTER CHE */
+{"Cyrillic_de", 0x06c4}, /* U+0434 CYRILLIC SMALL LETTER DE */
+{"Cyrillic_DE", 0x06e4}, /* U+0414 CYRILLIC CAPITAL LETTER DE */
+{"Cyrillic_dzhe", 0x06af}, /* U+045F CYRILLIC SMALL LETTER DZHE */
+{"Cyrillic_DZHE", 0x06bf}, /* U+040F CYRILLIC CAPITAL LETTER DZHE */
+{"Cyrillic_e", 0x06dc}, /* U+044D CYRILLIC SMALL LETTER E */
+{"Cyrillic_E", 0x06fc}, /* U+042D CYRILLIC CAPITAL LETTER E */
+{"Cyrillic_ef", 0x06c6}, /* U+0444 CYRILLIC SMALL LETTER EF */
+{"Cyrillic_EF", 0x06e6}, /* U+0424 CYRILLIC CAPITAL LETTER EF */
+{"Cyrillic_el", 0x06cc}, /* U+043B CYRILLIC SMALL LETTER EL */
+{"Cyrillic_EL", 0x06ec}, /* U+041B CYRILLIC CAPITAL LETTER EL */
+{"Cyrillic_em", 0x06cd}, /* U+043C CYRILLIC SMALL LETTER EM */
+{"Cyrillic_EM", 0x06ed}, /* U+041C CYRILLIC CAPITAL LETTER EM */
+{"Cyrillic_en", 0x06ce}, /* U+043D CYRILLIC SMALL LETTER EN */
+{"Cyrillic_EN", 0x06ee}, /* U+041D CYRILLIC CAPITAL LETTER EN */
+{"Cyrillic_er", 0x06d2}, /* U+0440 CYRILLIC SMALL LETTER ER */
+{"Cyrillic_ER", 0x06f2}, /* U+0420 CYRILLIC CAPITAL LETTER ER */
+{"Cyrillic_es", 0x06d3}, /* U+0441 CYRILLIC SMALL LETTER ES */
+{"Cyrillic_ES", 0x06f3}, /* U+0421 CYRILLIC CAPITAL LETTER ES */
+{"Cyrillic_ghe", 0x06c7}, /* U+0433 CYRILLIC SMALL LETTER GHE */
+{"Cyrillic_GHE", 0x06e7}, /* U+0413 CYRILLIC CAPITAL LETTER GHE */
+{"Cyrillic_ha", 0x06c8}, /* U+0445 CYRILLIC SMALL LETTER HA */
+{"Cyrillic_HA", 0x06e8}, /* U+0425 CYRILLIC CAPITAL LETTER HA */
+{"Cyrillic_hardsign", 0x06df}, /* U+044A CYRILLIC SMALL LETTER HARD SIGN */
+{"Cyrillic_HARDSIGN", 0x06ff}, /* U+042A CYRILLIC CAPITAL LETTER HARD SIGN */
+{"Cyrillic_i", 0x06c9}, /* U+0438 CYRILLIC SMALL LETTER I */
+{"Cyrillic_I", 0x06e9}, /* U+0418 CYRILLIC CAPITAL LETTER I */
+{"Cyrillic_ie", 0x06c5}, /* U+0435 CYRILLIC SMALL LETTER IE */
+{"Cyrillic_IE", 0x06e5}, /* U+0415 CYRILLIC CAPITAL LETTER IE */
+{"Cyrillic_io", 0x06a3}, /* U+0451 CYRILLIC SMALL LETTER IO */
+{"Cyrillic_IO", 0x06b3}, /* U+0401 CYRILLIC CAPITAL LETTER IO */
+{"Cyrillic_je", 0x06a8}, /* U+0458 CYRILLIC SMALL LETTER JE */
+{"Cyrillic_JE", 0x06b8}, /* U+0408 CYRILLIC CAPITAL LETTER JE */
+{"Cyrillic_ka", 0x06cb}, /* U+043A CYRILLIC SMALL LETTER KA */
+{"Cyrillic_KA", 0x06eb}, /* U+041A CYRILLIC CAPITAL LETTER KA */
+{"Cyrillic_lje", 0x06a9}, /* U+0459 CYRILLIC SMALL LETTER LJE */
+{"Cyrillic_LJE", 0x06b9}, /* U+0409 CYRILLIC CAPITAL LETTER LJE */
+{"Cyrillic_nje", 0x06aa}, /* U+045A CYRILLIC SMALL LETTER NJE */
+{"Cyrillic_NJE", 0x06ba}, /* U+040A CYRILLIC CAPITAL LETTER NJE */
+{"Cyrillic_o", 0x06cf}, /* U+043E CYRILLIC SMALL LETTER O */
+{"Cyrillic_O", 0x06ef}, /* U+041E CYRILLIC CAPITAL LETTER O */
+{"Cyrillic_pe", 0x06d0}, /* U+043F CYRILLIC SMALL LETTER PE */
+{"Cyrillic_PE", 0x06f0}, /* U+041F CYRILLIC CAPITAL LETTER PE */
+{"Cyrillic_sha", 0x06db}, /* U+0448 CYRILLIC SMALL LETTER SHA */
+{"Cyrillic_SHA", 0x06fb}, /* U+0428 CYRILLIC CAPITAL LETTER SHA */
+{"Cyrillic_shcha", 0x06dd}, /* U+0449 CYRILLIC SMALL LETTER SHCHA */
+{"Cyrillic_SHCHA", 0x06fd}, /* U+0429 CYRILLIC CAPITAL LETTER SHCHA */
+{"Cyrillic_shorti", 0x06ca}, /* U+0439 CYRILLIC SMALL LETTER SHORT I */
+{"Cyrillic_SHORTI", 0x06ea}, /* U+0419 CYRILLIC CAPITAL LETTER SHORT I */
+{"Cyrillic_softsign", 0x06d8}, /* U+044C CYRILLIC SMALL LETTER SOFT SIGN */
+{"Cyrillic_SOFTSIGN", 0x06f8}, /* U+042C CYRILLIC CAPITAL LETTER SOFT SIGN */
+{"Cyrillic_te", 0x06d4}, /* U+0442 CYRILLIC SMALL LETTER TE */
+{"Cyrillic_TE", 0x06f4}, /* U+0422 CYRILLIC CAPITAL LETTER TE */
+{"Cyrillic_tse", 0x06c3}, /* U+0446 CYRILLIC SMALL LETTER TSE */
+{"Cyrillic_TSE", 0x06e3}, /* U+0426 CYRILLIC CAPITAL LETTER TSE */
+{"Cyrillic_u", 0x06d5}, /* U+0443 CYRILLIC SMALL LETTER U */
+{"Cyrillic_U", 0x06f5}, /* U+0423 CYRILLIC CAPITAL LETTER U */
+{"Cyrillic_ve", 0x06d7}, /* U+0432 CYRILLIC SMALL LETTER VE */
+{"Cyrillic_VE", 0x06f7}, /* U+0412 CYRILLIC CAPITAL LETTER VE */
+{"Cyrillic_ya", 0x06d1}, /* U+044F CYRILLIC SMALL LETTER YA */
+{"Cyrillic_YA", 0x06f1}, /* U+042F CYRILLIC CAPITAL LETTER YA */
+{"Cyrillic_yeru", 0x06d9}, /* U+044B CYRILLIC SMALL LETTER YERU */
+{"Cyrillic_YERU", 0x06f9}, /* U+042B CYRILLIC CAPITAL LETTER YERU */
+{"Cyrillic_yu", 0x06c0}, /* U+044E CYRILLIC SMALL LETTER YU */
+{"Cyrillic_YU", 0x06e0}, /* U+042E CYRILLIC CAPITAL LETTER YU */
+{"Cyrillic_ze", 0x06da}, /* U+0437 CYRILLIC SMALL LETTER ZE */
+{"Cyrillic_ZE", 0x06fa}, /* U+0417 CYRILLIC CAPITAL LETTER ZE */
+{"Cyrillic_zhe", 0x06d6}, /* U+0436 CYRILLIC SMALL LETTER ZHE */
+{"Cyrillic_ZHE", 0x06f6}, /* U+0416 CYRILLIC CAPITAL LETTER ZHE */
+{"doubleacute", 0x01bd}, /* U+02DD DOUBLE ACUTE ACCENT */
+{"doublelowquotemark", 0x0afe}, /* U+201E DOUBLE LOW-9 QUOTATION MARK */
+{"downarrow", 0x08fe}, /* U+2193 DOWNWARDS ARROW */
+{"dstroke", 0x01f0}, /* U+0111 LATIN SMALL LETTER D WITH STROKE */
+{"Dstroke", 0x01d0}, /* U+0110 LATIN CAPITAL LETTER D WITH STROKE */
+{"eabovedot", 0x03ec}, /* U+0117 LATIN SMALL LETTER E WITH DOT ABOVE */
+{"Eabovedot", 0x03cc}, /* U+0116 LATIN CAPITAL LETTER E WITH DOT ABOVE */
+{"emacron", 0x03ba}, /* U+0113 LATIN SMALL LETTER E WITH MACRON */
+{"Emacron", 0x03aa}, /* U+0112 LATIN CAPITAL LETTER E WITH MACRON */
+{"endash", 0x0aaa}, /* U+2013 EN DASH */
+{"eng", 0x03bf}, /* U+014B LATIN SMALL LETTER ENG */
+{"ENG", 0x03bd}, /* U+014A LATIN CAPITAL LETTER ENG */
+{"Execute", 0xff62}, /* Execute, run, do */
+{"F16", 0xffcd},
+{"F17", 0xffce},
+{"F18", 0xffcf},
+{"F19", 0xffd0},
+{"F20", 0xffd1},
+{"F21", 0xffd2},
+{"F22", 0xffd3},
+{"F23", 0xffd4},
+{"F24", 0xffd5},
+{"F25", 0xffd6},
+{"F26", 0xffd7},
+{"F27", 0xffd8},
+{"F28", 0xffd9},
+{"F29", 0xffda},
+{"F30", 0xffdb},
+{"F31", 0xffdc},
+{"F32", 0xffdd},
+{"F33", 0xffde},
+{"F34", 0xffdf},
+{"F35", 0xffe0},
+{"fiveeighths", 0x0ac5}, /* U+215D VULGAR FRACTION FIVE EIGHTHS */
+{"gbreve", 0x02bb}, /* U+011F LATIN SMALL LETTER G WITH BREVE */
+{"Gbreve", 0x02ab}, /* U+011E LATIN CAPITAL LETTER G WITH BREVE */
+{"gcedilla", 0x03bb}, /* U+0123 LATIN SMALL LETTER G WITH CEDILLA */
+{"Gcedilla", 0x03ab}, /* U+0122 LATIN CAPITAL LETTER G WITH CEDILLA */
+{"Greek_OMEGA", 0x07d9}, /* U+03A9 GREEK CAPITAL LETTER OMEGA */
+{"Henkan_Mode", 0xff23}, /* Start/Stop Conversion */
+{"horizconnector", 0x08a3}, /*(U+2500 BOX DRAWINGS LIGHT HORIZONTAL)*/
+{"hstroke", 0x02b1}, /* U+0127 LATIN SMALL LETTER H WITH STROKE */
+{"Hstroke", 0x02a1}, /* U+0126 LATIN CAPITAL LETTER H WITH STROKE */
+{"Iabovedot", 0x02a9}, /* U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE */
+{"idotless", 0x02b9}, /* U+0131 LATIN SMALL LETTER DOTLESS I */
+{"imacron", 0x03ef}, /* U+012B LATIN SMALL LETTER I WITH MACRON */
+{"Imacron", 0x03cf}, /* U+012A LATIN CAPITAL LETTER I WITH MACRON */
+{"iogonek", 0x03e7}, /* U+012F LATIN SMALL LETTER I WITH OGONEK */
+{"Iogonek", 0x03c7}, /* U+012E LATIN CAPITAL LETTER I WITH OGONEK */
+{"ISO_First_Group", 0xfe0c},
+{"ISO_Last_Group", 0xfe0e},
+{"ISO_Next_Group", 0xfe08},
+{"kana_a", 0x04a7}, /* U+30A1 KATAKANA LETTER SMALL A */
+{"kana_A", 0x04b1}, /* U+30A2 KATAKANA LETTER A */
+{"kana_CHI", 0x04c1}, /* U+30C1 KATAKANA LETTER TI */
+{"kana_closingbracket", 0x04a3}, /* U+300D RIGHT CORNER BRACKET */
+{"kana_comma", 0x04a4}, /* U+3001 IDEOGRAPHIC COMMA */
+{"kana_conjunctive", 0x04a5}, /* U+30FB KATAKANA MIDDLE DOT */
+{"kana_e", 0x04aa}, /* U+30A7 KATAKANA LETTER SMALL E */
+{"kana_E", 0x04b4}, /* U+30A8 KATAKANA LETTER E */
+{"kana_FU", 0x04cc}, /* U+30D5 KATAKANA LETTER HU */
+{"kana_fullstop", 0x04a1}, /* U+3002 IDEOGRAPHIC FULL STOP */
+{"kana_HA", 0x04ca}, /* U+30CF KATAKANA LETTER HA */
+{"kana_HE", 0x04cd}, /* U+30D8 KATAKANA LETTER HE */
+{"kana_HI", 0x04cb}, /* U+30D2 KATAKANA LETTER HI */
+{"kana_HO", 0x04ce}, /* U+30DB KATAKANA LETTER HO */
+{"kana_i", 0x04a8}, /* U+30A3 KATAKANA LETTER SMALL I */
+{"kana_I", 0x04b2}, /* U+30A4 KATAKANA LETTER I */
+{"kana_KA", 0x04b6}, /* U+30AB KATAKANA LETTER KA */
+{"kana_KE", 0x04b9}, /* U+30B1 KATAKANA LETTER KE */
+{"kana_KI", 0x04b7}, /* U+30AD KATAKANA LETTER KI */
+{"kana_KO", 0x04ba}, /* U+30B3 KATAKANA LETTER KO */
+{"kana_KU", 0x04b8}, /* U+30AF KATAKANA LETTER KU */
+{"kana_MA", 0x04cf}, /* U+30DE KATAKANA LETTER MA */
+{"kana_ME", 0x04d2}, /* U+30E1 KATAKANA LETTER ME */
+{"kana_MI", 0x04d0}, /* U+30DF KATAKANA LETTER MI */
+{"kana_MO", 0x04d3}, /* U+30E2 KATAKANA LETTER MO */
+{"kana_MU", 0x04d1}, /* U+30E0 KATAKANA LETTER MU */
+{"kana_N", 0x04dd}, /* U+30F3 KATAKANA LETTER N */
+{"kana_NA", 0x04c5}, /* U+30CA KATAKANA LETTER NA */
+{"kana_NE", 0x04c8}, /* U+30CD KATAKANA LETTER NE */
+{"kana_NI", 0x04c6}, /* U+30CB KATAKANA LETTER NI */
+{"kana_NO", 0x04c9}, /* U+30CE KATAKANA LETTER NO */
+{"kana_NU", 0x04c7}, /* U+30CC KATAKANA LETTER NU */
+{"kana_o", 0x04ab}, /* U+30A9 KATAKANA LETTER SMALL O */
+{"kana_O", 0x04b5}, /* U+30AA KATAKANA LETTER O */
+{"kana_openingbracket", 0x04a2}, /* U+300C LEFT CORNER BRACKET */
+{"kana_RA", 0x04d7}, /* U+30E9 KATAKANA LETTER RA */
+{"kana_RE", 0x04da}, /* U+30EC KATAKANA LETTER RE */
+{"kana_RI", 0x04d8}, /* U+30EA KATAKANA LETTER RI */
+{"kana_RU", 0x04d9}, /* U+30EB KATAKANA LETTER RU */
+{"kana_SA", 0x04bb}, /* U+30B5 KATAKANA LETTER SA */
+{"kana_SE", 0x04be}, /* U+30BB KATAKANA LETTER SE */
+{"kana_SHI", 0x04bc}, /* U+30B7 KATAKANA LETTER SI */
+{"kana_SO", 0x04bf}, /* U+30BD KATAKANA LETTER SO */
+{"kana_SU", 0x04bd}, /* U+30B9 KATAKANA LETTER SU */
+{"kana_TA", 0x04c0}, /* U+30BF KATAKANA LETTER TA */
+{"kana_TE", 0x04c3}, /* U+30C6 KATAKANA LETTER TE */
+{"kana_TO", 0x04c4}, /* U+30C8 KATAKANA LETTER TO */
+{"kana_tsu", 0x04af}, /* U+30C3 KATAKANA LETTER SMALL TU */
+{"kana_TSU", 0x04c2}, /* U+30C4 KATAKANA LETTER TU */
+{"kana_u", 0x04a9}, /* U+30A5 KATAKANA LETTER SMALL U */
+{"kana_U", 0x04b3}, /* U+30A6 KATAKANA LETTER U */
+{"kana_WA", 0x04dc}, /* U+30EF KATAKANA LETTER WA */
+{"kana_WO", 0x04a6}, /* U+30F2 KATAKANA LETTER WO */
+{"kana_ya", 0x04ac}, /* U+30E3 KATAKANA LETTER SMALL YA */
+{"kana_YA", 0x04d4}, /* U+30E4 KATAKANA LETTER YA */
+{"kana_yo", 0x04ae}, /* U+30E7 KATAKANA LETTER SMALL YO */
+{"kana_YO", 0x04d6}, /* U+30E8 KATAKANA LETTER YO */
+{"kana_yu", 0x04ad}, /* U+30E5 KATAKANA LETTER SMALL YU */
+{"kana_YU", 0x04d5}, /* U+30E6 KATAKANA LETTER YU */
+{"Kanji", 0xff21}, /* Kanji, Kanji convert */
+{"kcedilla", 0x03f3}, /* U+0137 LATIN SMALL LETTER K WITH CEDILLA */
+{"Kcedilla", 0x03d3}, /* U+0136 LATIN CAPITAL LETTER K WITH CEDILLA */
+{"kra", 0x03a2}, /* U+0138 LATIN SMALL LETTER KRA */
+{"lcedilla", 0x03b6}, /* U+013C LATIN SMALL LETTER L WITH CEDILLA */
+{"Lcedilla", 0x03a6}, /* U+013B LATIN CAPITAL LETTER L WITH CEDILLA */
+{"leftarrow", 0x08fb}, /* U+2190 LEFTWARDS ARROW */
+{"leftdoublequotemark", 0x0ad2}, /* U+201C LEFT DOUBLE QUOTATION MARK */
+{"Macedonia_dse", 0x06a5}, /* U+0455 CYRILLIC SMALL LETTER DZE */
+{"Macedonia_DSE", 0x06b5}, /* U+0405 CYRILLIC CAPITAL LETTER DZE */
+{"Macedonia_gje", 0x06a2}, /* U+0453 CYRILLIC SMALL LETTER GJE */
+{"Macedonia_GJE", 0x06b2}, /* U+0403 CYRILLIC CAPITAL LETTER GJE */
+{"Macedonia_kje", 0x06ac}, /* U+045C CYRILLIC SMALL LETTER KJE */
+{"Macedonia_KJE", 0x06bc}, /* U+040C CYRILLIC CAPITAL LETTER KJE */
+{"ncedilla", 0x03f1}, /* U+0146 LATIN SMALL LETTER N WITH CEDILLA */
+{"Ncedilla", 0x03d1}, /* U+0145 LATIN CAPITAL LETTER N WITH CEDILLA */
+{"oe", 0x13bd}, /* U+0153 LATIN SMALL LIGATURE OE */
+{"OE", 0x13bc}, /* U+0152 LATIN CAPITAL LIGATURE OE */
+{"ogonek", 0x01b2}, /* U+02DB OGONEK */
+{"omacron", 0x03f2}, /* U+014D LATIN SMALL LETTER O WITH MACRON */
+{"Omacron", 0x03d2}, /* U+014C LATIN CAPITAL LETTER O WITH MACRON */
+{"oneeighth", 0x0ac3}, /* U+215B VULGAR FRACTION ONE EIGHTH */
+{"rcedilla", 0x03b3}, /* U+0157 LATIN SMALL LETTER R WITH CEDILLA */
+{"Rcedilla", 0x03a3}, /* U+0156 LATIN CAPITAL LETTER R WITH CEDILLA */
+{"rightarrow", 0x08fd}, /* U+2192 RIGHTWARDS ARROW */
+{"rightdoublequotemark", 0x0ad3}, /* U+201D RIGHT DOUBLE QUOTATION MARK */
+{"Scaron", 0x01a9}, /* U+0160 LATIN CAPITAL LETTER S WITH CARON */
+{"scedilla", 0x01ba}, /* U+015F LATIN SMALL LETTER S WITH CEDILLA */
+{"Scedilla", 0x01aa}, /* U+015E LATIN CAPITAL LETTER S WITH CEDILLA */
+{"semivoicedsound", 0x04df}, /* U+309C KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+{"seveneighths", 0x0ac6}, /* U+215E VULGAR FRACTION SEVEN EIGHTHS */
+{"Thai_baht", 0x0ddf}, /* U+0E3F THAI CURRENCY SYMBOL BAHT */
+{"Thai_bobaimai", 0x0dba}, /* U+0E1A THAI CHARACTER BO BAIMAI */
+{"Thai_chochan", 0x0da8}, /* U+0E08 THAI CHARACTER CHO CHAN */
+{"Thai_chochang", 0x0daa}, /* U+0E0A THAI CHARACTER CHO CHANG */
+{"Thai_choching", 0x0da9}, /* U+0E09 THAI CHARACTER CHO CHING */
+{"Thai_chochoe", 0x0dac}, /* U+0E0C THAI CHARACTER CHO CHOE */
+{"Thai_dochada", 0x0dae}, /* U+0E0E THAI CHARACTER DO CHADA */
+{"Thai_dodek", 0x0db4}, /* U+0E14 THAI CHARACTER DO DEK */
+{"Thai_fofa", 0x0dbd}, /* U+0E1D THAI CHARACTER FO FA */
+{"Thai_fofan", 0x0dbf}, /* U+0E1F THAI CHARACTER FO FAN */
+{"Thai_hohip", 0x0dcb}, /* U+0E2B THAI CHARACTER HO HIP */
+{"Thai_honokhuk", 0x0dce}, /* U+0E2E THAI CHARACTER HO NOKHUK */
+{"Thai_khokhai", 0x0da2}, /* U+0E02 THAI CHARACTER KHO KHAI */
+{"Thai_khokhon", 0x0da5}, /* U+0E05 THAI CHARACTER KHO KHON */
+{"Thai_khokhuat", 0x0da3}, /* U+0E03 THAI CHARACTER KHO KHUAT */
+{"Thai_khokhwai", 0x0da4}, /* U+0E04 THAI CHARACTER KHO KHWAI */
+{"Thai_khorakhang", 0x0da6}, /* U+0E06 THAI CHARACTER KHO RAKHANG */
+{"Thai_kokai", 0x0da1}, /* U+0E01 THAI CHARACTER KO KAI */
+{"Thai_lakkhangyao", 0x0de5}, /* U+0E45 THAI CHARACTER LAKKHANGYAO */
+{"Thai_lekchet", 0x0df7}, /* U+0E57 THAI DIGIT SEVEN */
+{"Thai_lekha", 0x0df5}, /* U+0E55 THAI DIGIT FIVE */
+{"Thai_lekhok", 0x0df6}, /* U+0E56 THAI DIGIT SIX */
+{"Thai_lekkao", 0x0df9}, /* U+0E59 THAI DIGIT NINE */
+{"Thai_leknung", 0x0df1}, /* U+0E51 THAI DIGIT ONE */
+{"Thai_lekpaet", 0x0df8}, /* U+0E58 THAI DIGIT EIGHT */
+{"Thai_leksam", 0x0df3}, /* U+0E53 THAI DIGIT THREE */
+{"Thai_leksi", 0x0df4}, /* U+0E54 THAI DIGIT FOUR */
+{"Thai_leksong", 0x0df2}, /* U+0E52 THAI DIGIT TWO */
+{"Thai_leksun", 0x0df0}, /* U+0E50 THAI DIGIT ZERO */
+{"Thai_lochula", 0x0dcc}, /* U+0E2C THAI CHARACTER LO CHULA */
+{"Thai_loling", 0x0dc5}, /* U+0E25 THAI CHARACTER LO LING */
+{"Thai_lu", 0x0dc6}, /* U+0E26 THAI CHARACTER LU */
+{"Thai_maichattawa", 0x0deb}, /* U+0E4B THAI CHARACTER MAI CHATTAWA */
+{"Thai_maiek", 0x0de8}, /* U+0E48 THAI CHARACTER MAI EK */
+{"Thai_maihanakat", 0x0dd1}, /* U+0E31 THAI CHARACTER MAI HAN-AKAT */
+{"Thai_maitaikhu", 0x0de7}, /* U+0E47 THAI CHARACTER MAITAIKHU */
+{"Thai_maitho", 0x0de9}, /* U+0E49 THAI CHARACTER MAI THO */
+{"Thai_maitri", 0x0dea}, /* U+0E4A THAI CHARACTER MAI TRI */
+{"Thai_maiyamok", 0x0de6}, /* U+0E46 THAI CHARACTER MAIYAMOK */
+{"Thai_moma", 0x0dc1}, /* U+0E21 THAI CHARACTER MO MA */
+{"Thai_ngongu", 0x0da7}, /* U+0E07 THAI CHARACTER NGO NGU */
+{"Thai_nikhahit", 0x0ded}, /* U+0E4D THAI CHARACTER NIKHAHIT */
+{"Thai_nonen", 0x0db3}, /* U+0E13 THAI CHARACTER NO NEN */
+{"Thai_nonu", 0x0db9}, /* U+0E19 THAI CHARACTER NO NU */
+{"Thai_oang", 0x0dcd}, /* U+0E2D THAI CHARACTER O ANG */
+{"Thai_paiyannoi", 0x0dcf}, /* U+0E2F THAI CHARACTER PAIYANNOI */
+{"Thai_phinthu", 0x0dda}, /* U+0E3A THAI CHARACTER PHINTHU */
+{"Thai_phophan", 0x0dbe}, /* U+0E1E THAI CHARACTER PHO PHAN */
+{"Thai_phophung", 0x0dbc}, /* U+0E1C THAI CHARACTER PHO PHUNG */
+{"Thai_phosamphao", 0x0dc0}, /* U+0E20 THAI CHARACTER PHO SAMPHAO */
+{"Thai_popla", 0x0dbb}, /* U+0E1B THAI CHARACTER PO PLA */
+{"Thai_rorua", 0x0dc3}, /* U+0E23 THAI CHARACTER RO RUA */
+{"Thai_ru", 0x0dc4}, /* U+0E24 THAI CHARACTER RU */
+{"Thai_saraa", 0x0dd0}, /* U+0E30 THAI CHARACTER SARA A */
+{"Thai_saraaa", 0x0dd2}, /* U+0E32 THAI CHARACTER SARA AA */
+{"Thai_saraae", 0x0de1}, /* U+0E41 THAI CHARACTER SARA AE */
+{"Thai_saraaimaimalai", 0x0de4}, /* U+0E44 THAI CHARACTER SARA AI MAIMALAI */
+{"Thai_saraaimaimuan", 0x0de3}, /* U+0E43 THAI CHARACTER SARA AI MAIMUAN */
+{"Thai_saraam", 0x0dd3}, /* U+0E33 THAI CHARACTER SARA AM */
+{"Thai_sarae", 0x0de0}, /* U+0E40 THAI CHARACTER SARA E */
+{"Thai_sarai", 0x0dd4}, /* U+0E34 THAI CHARACTER SARA I */
+{"Thai_saraii", 0x0dd5}, /* U+0E35 THAI CHARACTER SARA II */
+{"Thai_sarao", 0x0de2}, /* U+0E42 THAI CHARACTER SARA O */
+{"Thai_sarau", 0x0dd8}, /* U+0E38 THAI CHARACTER SARA U */
+{"Thai_saraue", 0x0dd6}, /* U+0E36 THAI CHARACTER SARA UE */
+{"Thai_sarauee", 0x0dd7}, /* U+0E37 THAI CHARACTER SARA UEE */
+{"Thai_sarauu", 0x0dd9}, /* U+0E39 THAI CHARACTER SARA UU */
+{"Thai_sorusi", 0x0dc9}, /* U+0E29 THAI CHARACTER SO RUSI */
+{"Thai_sosala", 0x0dc8}, /* U+0E28 THAI CHARACTER SO SALA */
+{"Thai_soso", 0x0dab}, /* U+0E0B THAI CHARACTER SO SO */
+{"Thai_sosua", 0x0dca}, /* U+0E2A THAI CHARACTER SO SUA */
+{"Thai_thanthakhat", 0x0dec}, /* U+0E4C THAI CHARACTER THANTHAKHAT */
+{"Thai_thonangmontho", 0x0db1}, /* U+0E11 THAI CHARACTER THO NANGMONTHO */
+{"Thai_thophuthao", 0x0db2}, /* U+0E12 THAI CHARACTER THO PHUTHAO */
+{"Thai_thothahan", 0x0db7}, /* U+0E17 THAI CHARACTER THO THAHAN */
+{"Thai_thothan", 0x0db0}, /* U+0E10 THAI CHARACTER THO THAN */
+{"Thai_thothong", 0x0db8}, /* U+0E18 THAI CHARACTER THO THONG */
+{"Thai_thothung", 0x0db6}, /* U+0E16 THAI CHARACTER THO THUNG */
+{"Thai_topatak", 0x0daf}, /* U+0E0F THAI CHARACTER TO PATAK */
+{"Thai_totao", 0x0db5}, /* U+0E15 THAI CHARACTER TO TAO */
+{"Thai_wowaen", 0x0dc7}, /* U+0E27 THAI CHARACTER WO WAEN */
+{"Thai_yoyak", 0x0dc2}, /* U+0E22 THAI CHARACTER YO YAK */
+{"Thai_yoying", 0x0dad}, /* U+0E0D THAI CHARACTER YO YING */
+{"threeeighths", 0x0ac4}, /* U+215C VULGAR FRACTION THREE EIGHTHS */
+{"trademark", 0x0ac9}, /* U+2122 TRADE MARK SIGN */
+{"tslash", 0x03bc}, /* U+0167 LATIN SMALL LETTER T WITH STROKE */
+{"Tslash", 0x03ac}, /* U+0166 LATIN CAPITAL LETTER T WITH STROKE */
+{"umacron", 0x03fe}, /* U+016B LATIN SMALL LETTER U WITH MACRON */
+{"Umacron", 0x03de}, /* U+016A LATIN CAPITAL LETTER U WITH MACRON */
+{"uogonek", 0x03f9}, /* U+0173 LATIN SMALL LETTER U WITH OGONEK */
+{"Uogonek", 0x03d9}, /* U+0172 LATIN CAPITAL LETTER U WITH OGONEK */
+{"uparrow", 0x08fc}, /* U+2191 UPWARDS ARROW */
+{"voicedsound", 0x04de}, /* U+309B KATAKANA-HIRAGANA VOICED SOUND MARK */
+{"Zcaron", 0x01ae}, /* U+017D LATIN CAPITAL LETTER Z WITH CARON */
+
{NULL,0},
};