summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorSeokYeon Hwang <syeon.hwang@samsung.com>2014-04-21 15:32:27 +0900
committerSeokYeon Hwang <syeon.hwang@samsung.com>2014-04-21 15:57:17 +0900
commitc38a355e67e966e447e3b58b897daffda70e2801 (patch)
tree3a90ebbaf8bf89014212325e374e5073f074b014 /ui
parent3d8e749baf4b053639581bc11e5664dc182e621d (diff)
parenta9e8aeb3755bccb7b51174adcf4a3fc427e0d147 (diff)
downloadqemu-c38a355e67e966e447e3b58b897daffda70e2801.tar.gz
qemu-c38a355e67e966e447e3b58b897daffda70e2801.tar.bz2
qemu-c38a355e67e966e447e3b58b897daffda70e2801.zip
Merge branch 'upstream_qemu_2.0' into tizen
Initial merge Signed-off-by: SeokYeon Hwang <syeon.hwang@samsung.com> Conflicts: .gitignore Makefile.target block/raw-win32.c configure cpu-exec.c cpus.c hw/9pfs/virtio-9p-device.c hw/i386/pc_piix.c include/exec/cpu-defs.h include/qemu-common.h include/sysemu/sysemu.h include/ui/console.h tcg/i386/tcg-target.c tcg/tcg.h ui/console.c ui/input.c util/oslib-posix.c vl.c Change-Id: I52df9052942eea8d541bde75fd936e58a9f2f8ff
Diffstat (limited to 'ui')
-rw-r--r--ui/Makefile.objs6
-rw-r--r--ui/cocoa.m182
-rw-r--r--ui/console.c88
-rw-r--r--ui/curses.c47
-rw-r--r--ui/gtk.c224
-rw-r--r--ui/input-legacy.c468
-rw-r--r--ui/input.c707
-rw-r--r--ui/sdl.c128
-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.c22
-rw-r--r--ui/spice-display.c58
-rw-r--r--ui/spice-input.c84
-rw-r--r--ui/vnc-enc-tight.c2
-rw-r--r--ui/vnc-jobs.c8
-rw-r--r--ui/vnc.c300
-rw-r--r--ui/vnc.h11
18 files changed, 2526 insertions, 912 deletions
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index f33be47576..6f2294efda 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -7,14 +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)/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 be491794dc..f20fd1ffa2 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 e4205cd9d7..311e13c6bf 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -27,13 +27,13 @@
#include "qemu/timer.h"
#include "qmp-commands.h"
#include "sysemu/char.h"
+#include "trace.h"
#ifdef SDL_THREAD
#include <pthread.h>
extern pthread_mutex_t sdl_mutex;
#endif
-//#define DEBUG_CONSOLE
#define DEFAULT_BACKSCROLL 512
#define MAX_CONSOLES 12
#define CONSOLE_CURSOR_PERIOD 500
@@ -129,6 +129,8 @@ struct QemuConsole {
/* Graphic console state. */
Object *device;
+ uint32_t head;
+ QemuUIInfo ui_info;
const GraphicHwOps *hw_ops;
void *hw;
@@ -166,7 +168,7 @@ struct QemuConsole {
};
struct DisplayState {
- struct QEMUTimer *gui_timer;
+ QEMUTimer *gui_timer;
uint64_t last_update;
uint64_t update_interval;
bool refreshing;
@@ -871,10 +873,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':
@@ -988,9 +988,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;
@@ -1187,7 +1185,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))) {
@@ -1361,6 +1364,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;
@@ -1595,7 +1608,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)
{
@@ -1613,6 +1626,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);
@@ -1627,10 +1642,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++) {
@@ -1639,9 +1655,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;
}
@@ -1667,6 +1689,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;
diff --git a/ui/curses.c b/ui/curses.c
index dbc3d5ec73..b044790e43 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
@@ -274,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)
diff --git a/ui/gtk.c b/ui/gtk.c
index b5f4f0bd40..00fbbccb34 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,26 +54,25 @@
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>
#include <locale.h>
+#if defined(CONFIG_VTE)
#include <vte/vte.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)
@@ -107,8 +110,10 @@ typedef struct VirtualConsole
{
GtkWidget *menu_item;
GtkWidget *terminal;
+#if defined(CONFIG_VTE)
GtkWidget *scrolled_window;
CharDriverState *chr;
+#endif
int fd;
} VirtualConsole;
@@ -151,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;
@@ -196,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);
@@ -283,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;
}
}
@@ -302,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,
@@ -345,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,
@@ -360,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),
@@ -396,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);
@@ -464,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 **/
@@ -586,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;
@@ -608,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;
@@ -665,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;
}
}
@@ -677,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;
@@ -731,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]) {
@@ -742,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)
@@ -955,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);
@@ -980,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 */
@@ -991,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);
@@ -1011,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
}
@@ -1062,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;
@@ -1071,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);
@@ -1116,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;
@@ -1141,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;
@@ -1156,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];
@@ -1229,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;
}
@@ -1251,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",
@@ -1443,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;
@@ -1522,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 0000000000..1aa2605b75
--- /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 2c56fe9988..6d678d1ed6 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -1,40 +1,13 @@
-/*
- * 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.
- */
-
-#if defined CONFIG_MARU && defined CONFIG_LINUX
-#include <pthread.h>
-#endif
-
#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 "qmp-commands.h"
+#include "trace.h"
+#include "ui/input.h"
+#include "ui/console.h"
#if defined CONFIG_MARU && defined CONFIG_LINUX
+#include <pthread.h>
+
extern void maru_hwkey_event(int event_type, int keycode);
extern void do_rotation_event(int rotation_type);
extern void do_host_kbd_enable(bool on);
@@ -47,418 +20,254 @@ void* tizen_close_thread(void* data);
//MULTI_DEBUG_CHANNEL(tizen, input);
-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 QemuInputHandlerState {
+ DeviceState *dev;
+ QemuInputHandler *handler;
+ int id;
+ int events;
+ QTAILQ_ENTRY(QemuInputHandlerState) node;
};
-
-struct QEMUPutKbdEntry {
- QEMUPutKBDEvent *put_kbd;
- void *opaque;
- QTAILQ_ENTRY(QEMUPutKbdEntry) next;
-};
-
-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(, 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 = 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;
+ 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 */
- timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
- 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));
+ QemuInputHandlerState *s;
- 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++;
-
- 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 (QTAILQ_EMPTY(&mouse_handlers)) {
- return;
+ if (size_in < 2) {
+ return size_out / 2;
}
-#if defined (CONFIG_MARU)
+#if 0 //defined (CONFIG_MARU)
QTAILQ_FOREACH(entry, &mouse_handlers, node) {
/* if mouse event is wheelup ,wheeldown or move
then go to ps2 mouse event(index == 0) */
@@ -477,64 +286,64 @@ void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
//INFO("input device: %s, event: %d\n", entry->qemu_put_mouse_event_name, buttons_state);
}
-#else
-
- entry = QTAILQ_FIRST(&mouse_handlers);
-
- mouse_event = entry->qemu_put_mouse_event;
- mouse_event_opaque = entry->qemu_put_mouse_event_opaque;
#endif
- if (mouse_event) {
- if (entry->qemu_put_mouse_event_absolute) {
- width = 0x7fff;
- height = 0x7fff;
- } else {
- width = graphic_width - 1;
- height = graphic_height - 1;
- }
+ return (int64_t)value * (size_out - 1) / (size_in - 1);
+}
- 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;
- }
- }
+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;
}
-int kbd_mouse_is_absolute(void)
+void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value)
{
- if (QTAILQ_EMPTY(&mouse_handlers)) {
- return 0;
- }
+ InputEvent *evt;
+ evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value);
+ qemu_input_event_send(src, evt);
+ qapi_free_InputEvent(evt);
+}
- return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute;
+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_has_absolute(void)
+void qemu_input_check_mode_change(void)
{
- QEMUPutMouseEntry *entry;
+ static int current_is_absolute;
+ int is_absolute;
- QTAILQ_FOREACH(entry, &mouse_handlers, node) {
- if (entry->qemu_put_mouse_event_absolute) {
- return 1;
- }
+ 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 0;
+ current_is_absolute = is_absolute;
+}
+
+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);
}
#if defined CONFIG_MARU && defined CONFIG_LINUX
@@ -595,19 +404,24 @@ void tizen_close_put_type(int type)
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;
}
@@ -617,36 +431,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/sdl.c b/ui/sdl.c
index 135d6a9b2b..5709b56c14 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"
@@ -261,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;
}
}
@@ -271,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;
}
@@ -312,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)
@@ -360,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 {
@@ -373,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);
@@ -395,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);
@@ -425,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)) {
@@ -440,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)
@@ -694,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 ||
@@ -707,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);
}
}
@@ -717,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);
}
}
@@ -760,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) {
@@ -832,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();
@@ -863,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);
}
@@ -966,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 0000000000..5a12f4543a
--- /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 0000000000..7506e2e13f
--- /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 ee904805da..599d9fc64d 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 e4d533d4c4..4cce3b38c0 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -47,6 +47,7 @@ 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;
static QemuThread me;
@@ -622,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();
}
}
@@ -776,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
@@ -902,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 f23a31854d..ce6b220f55 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -126,23 +126,6 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
spice_qxl_wakeup(&ssd->qxl);
}
-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;
-}
-
static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
QXLRect *rect)
{
@@ -371,6 +354,7 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
DisplaySurface *surface)
{
SimpleSpiceUpdate *update;
+ bool need_destroy;
dprint(1, "%s/%d:\n", __func__, ssd->qxl.id);
@@ -383,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++;
@@ -554,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 = {
@@ -627,8 +639,6 @@ static void qemu_spice_display_init_one(QemuConsole *con)
ssd->dcl.ops = &display_listener_ops;
ssd->dcl.con = con;
register_displaychangelistener(&ssd->dcl);
-
- qemu_spice_create_host_primary(ssd);
}
void qemu_spice_display_init(void)
diff --git a/ui/spice-input.c b/ui/spice-input.c
index b7d0b1c31d..cd0cde2400 100644
--- a/ui/spice-input.c
+++ b/ui/spice-input.c
@@ -26,6 +26,8 @@
#include "qemu-common.h"
#include "ui/qemu-spice.h"
#include "ui/console.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
#ifdef CONFIG_MARU
extern int get_emul_vm_base_port(void);
@@ -36,6 +38,7 @@ extern int get_emul_vm_base_port(void);
typedef struct QemuSpiceKbd {
SpiceKbdInstance sin;
int ledstate;
+ bool emul0;
} QemuSpiceKbd;
static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag);
@@ -51,9 +54,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)
@@ -188,41 +206,52 @@ static void qemu_get_sdb_port(SpiceSdbPortInstance *sin, int* sdb_port)
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 = {
@@ -253,9 +282,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();
}
@@ -264,7 +294,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,
@@ -272,7 +303,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 = {
@@ -289,7 +321,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-enc-tight.c b/ui/vnc-enc-tight.c
index e6966aebc3..59b59c0c79 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-jobs.c b/ui/vnc-jobs.c
index 2d3fce8155..68f3d773d9 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.c b/ui/vnc.c
index 5601cc34ef..5925774509 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 6e9921387f..8da81b8d6e 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;