ui: add kbd stats tracker.

ui: gtk scroll fixes.
 ui: egl cursor scale fix.
 ui: more sdl1 cleanup.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJcWWwmAAoJEEy22O7T6HE473kQAIlRHR4xHWTgsVD4sxj7qRqv
 vN+OHCz/Z+bRPUaP2xR583Bhw67J1Z6KjBoZj67WDHBXG8gjV5HwQvM0ahGrH4iB
 70InTLe2EQkS34lFGwjChDd3uVV3jgPXItILYr8UeAJSMpKF95mx/WL9UZT41Q0n
 BiOEwy33EpqV6vZXRrWLjzeqZEiIKF66/flmVjcJDPk09gN5dUP80+0bXyfZe4qs
 7ChvAk/t8ycgw1P60MsdYadzG7Vqp7tezbMSba+Cfm/SSeAbZJQVcsV4og6K0ppv
 fidLAzH51BEHSDXRKdzCg5L8jNusgnXhBZ5eh0RrpEEodOom7XdbypDTJn2uipG1
 11911D5+8Z5iLplgMqLtNtktA8UoSZxIVuzG7wrCV4dvRx45BHwyV+aw4Y0LYrOi
 +jB9pLMfNp0FcqYWk2VcXQKSkC6wGBz0E0iqcNKu1oZBpTWs400hw+cN+ACyxWg9
 HklPxST/wZSrlTg7WrQmSaqC+rAtUkPYKwTnWEOjncxUu+C5t2kLFNyxbvrg+z4i
 6c+tAiJicjMVhTjvGg905l/0g8FNNVUDWGpJ1DGuD+03l6mZgyWzc8IToClAHzdl
 RpZwZ6tdshV9P+l3eLzVvk2BIYcs6/MA4z/Adk/YbrXYty9FGk0WUKgfJqkyfzFx
 7yn56KpBGHKm2Pvca+qO
 =Q86o
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/ui-20190205-pull-request' into staging

ui: add kbd stats tracker.
ui: gtk scroll fixes.
ui: egl cursor scale fix.
ui: more sdl1 cleanup.

# gpg: Signature made Tue 05 Feb 2019 10:57:42 GMT
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/ui-20190205-pull-request:
  keymap: fix keyup mappings
  keymap: pass full keyboard state to keysym2scancode
  kbd-state: use state tracker for vnc
  kbd-state: use state tracker for gtk
  sdl2: use only QKeyCode in sdl2_process_key()
  kbd-state: use state tracker for sdl2
  sdl2: remove sdl2_reset_keys() function
  kbd-state: add keyboard state tracker
  ui/egl-helpers: Augment parameter list of egl_texture_blend() to convey scales of viewport
  ui/cocoa.m: Fix macOS 10.14 deprecation warnings
  ui/sdl_keysym: Remove obsolete SDL1.2 related code
  ui: listen for GDK_SMOOTH_SCROLL events
  ui: don't send any event if delta_y == 0
  Remove deprecated -no-frame option

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-02-05 14:01:29 +00:00
commit 01a9a51ffa
24 changed files with 360 additions and 533 deletions

View File

@ -102,7 +102,6 @@ extern const char *keyboard_layout;
extern int win2k_install_hack; extern int win2k_install_hack;
extern int alt_grab; extern int alt_grab;
extern int ctrl_grab; extern int ctrl_grab;
extern int no_frame;
extern int smp_cpus; extern int smp_cpus;
extern unsigned int max_cpus; extern unsigned int max_cpus;
extern int cursor_hide; extern int cursor_hide;

View File

@ -27,7 +27,7 @@ void egl_fb_read(void *dst, egl_fb *src);
void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip); void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip);
void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
int x, int y); int x, int y, double scale_x, double scale_y);
#ifdef CONFIG_OPENGL_DMABUF #ifdef CONFIG_OPENGL_DMABUF

View File

@ -22,6 +22,7 @@
#include <gdk/gdkwayland.h> #include <gdk/gdkwayland.h>
#endif #endif
#include "ui/kbd-state.h"
#if defined(CONFIG_OPENGL) #if defined(CONFIG_OPENGL)
#include "ui/egl-helpers.h" #include "ui/egl-helpers.h"
#include "ui/egl-context.h" #include "ui/egl-context.h"
@ -32,6 +33,7 @@ typedef struct GtkDisplayState GtkDisplayState;
typedef struct VirtualGfxConsole { typedef struct VirtualGfxConsole {
GtkWidget *drawing_area; GtkWidget *drawing_area;
DisplayChangeListener dcl; DisplayChangeListener dcl;
QKbdState *kbd;
DisplaySurface *ds; DisplaySurface *ds;
pixman_image_t *convert; pixman_image_t *convert;
cairo_surface_t *surface; cairo_surface_t *surface;

101
include/ui/kbd-state.h Normal file
View File

@ -0,0 +1,101 @@
/*
* This work is licensed under the terms of the GNU GPL, version 2 or
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
#ifndef QEMU_UI_KBD_STATE_H
#define QEMU_UI_KBD_STATE_H 1
#include "qapi/qapi-types-ui.h"
typedef enum QKbdModifier QKbdModifier;
enum QKbdModifier {
QKBD_MOD_NONE = 0,
QKBD_MOD_SHIFT,
QKBD_MOD_CTRL,
QKBD_MOD_ALT,
QKBD_MOD_ALTGR,
QKBD_MOD_NUMLOCK,
QKBD_MOD_CAPSLOCK,
QKBD_MOD__MAX
};
typedef struct QKbdState QKbdState;
/**
* qkbd_state_init: init keyboard state tracker.
*
* Allocates and initializes keyboard state struct.
*
* @con: QemuConsole for this state tracker. Gets passed down to
* qemu_input_*() functions when sending key events to the guest.
*/
QKbdState *qkbd_state_init(QemuConsole *con);
/**
* qkbd_state_free: free keyboard tracker state.
*
* @kbd: state tracker state.
*/
void qkbd_state_free(QKbdState *kbd);
/**
* qkbd_state_key_event: process key event.
*
* Update keyboard state, send event to the guest.
*
* This function takes care to not send suspious events (keyup event
* for a key not pressed for example).
*
* @kbd: state tracker state.
* @qcode: the key pressed or released.
* @down: true for key down events, false otherwise.
*/
void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down);
/**
* qkbd_state_set_delay: set key press delay.
*
* When set the specified delay will be added after each key event,
* using qemu_input_event_send_key_delay().
*
* @kbd: state tracker state.
* @delay_ms: the delay in miliseconds.
*/
void qkbd_state_set_delay(QKbdState *kbd, int delay_ms);
/**
* qkbd_state_key_get: get key state.
*
* Returns true when the key is down.
*
* @kbd: state tracker state.
* @qcode: the key to query.
*/
bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode);
/**
* qkbd_state_modifier_get: get modifier state.
*
* Returns true when the modifier is active.
*
* @kbd: state tracker state.
* @mod: the modifier to query.
*/
bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod);
/**
* qkbd_state_lift_all_keys: lift all pressed keys.
*
* This sends key up events to the guest for all keys which are in
* down state.
*
* @kbd: state tracker state.
*/
void qkbd_state_lift_all_keys(QKbdState *kbd);
#endif /* QEMU_UI_KBD_STATE_H */

View File

@ -10,6 +10,7 @@
# include <SDL_image.h> # include <SDL_image.h>
#endif #endif
#include "ui/kbd-state.h"
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
# include "ui/egl-helpers.h" # include "ui/egl-helpers.h"
#endif #endif
@ -30,6 +31,7 @@ struct sdl2_console {
int idle_counter; int idle_counter;
int ignore_hotkeys; int ignore_hotkeys;
SDL_GLContext winctx; SDL_GLContext winctx;
QKbdState *kbd;
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
QemuGLShader *gls; QemuGLShader *gls;
egl_fb guest_fb; egl_fb guest_fb;
@ -44,7 +46,6 @@ void sdl2_window_destroy(struct sdl2_console *scon);
void sdl2_window_resize(struct sdl2_console *scon); void sdl2_window_resize(struct sdl2_console *scon);
void sdl2_poll_events(struct sdl2_console *scon); void sdl2_poll_events(struct sdl2_console *scon);
void sdl2_reset_keys(struct sdl2_console *scon);
void sdl2_process_key(struct sdl2_console *scon, void sdl2_process_key(struct sdl2_console *scon,
SDL_KeyboardEvent *ev); SDL_KeyboardEvent *ev);

View File

@ -37,12 +37,6 @@ would automatically enable USB support on the machine type.
If using the new syntax, USB support must be explicitly If using the new syntax, USB support must be explicitly
enabled via the ``-machine usb=on'' argument. enabled via the ``-machine usb=on'' argument.
@subsection -no-frame (since 2.12.0)
The @code{--no-frame} argument works with SDL 1.2 only. The other user
interfaces never implemented this in the first place. So this will be
removed together with SDL 1.2 support.
@subsection -virtioconsole (since 3.0.0) @subsection -virtioconsole (since 3.0.0)
Option @option{-virtioconsole} has been replaced by Option @option{-virtioconsole} has been replaced by

View File

@ -1294,17 +1294,6 @@ mode using a curses/ncurses interface. Nothing is displayed in graphical
mode. mode.
ETEXI ETEXI
DEF("no-frame", 0, QEMU_OPTION_no_frame,
"-no-frame open SDL window without a frame and window decorations\n",
QEMU_ARCH_ALL)
STEXI
@item -no-frame
@findex -no-frame
Do not use decorations for SDL windows and start them using the whole
available screen space. This makes the using QEMU in a dedicated desktop
workspace more convenient.
ETEXI
DEF("alt-grab", 0, QEMU_OPTION_alt_grab, DEF("alt-grab", 0, QEMU_OPTION_alt_grab,
"-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n", "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n",
QEMU_ARCH_ALL) QEMU_ARCH_ALL)

View File

@ -8,7 +8,7 @@ vnc-obj-y += vnc-ws.o
vnc-obj-y += vnc-jobs.o vnc-obj-y += vnc-jobs.o
common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
common-obj-y += input.o input-keymap.o input-legacy.o common-obj-y += input.o input-keymap.o input-legacy.o kbd-state.o
common-obj-$(CONFIG_LINUX) += input-linux.o common-obj-$(CONFIG_LINUX) += input-linux.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_COCOA) += cocoa.o

View File

@ -54,6 +54,9 @@
#ifndef MAC_OS_X_VERSION_10_12 #ifndef MAC_OS_X_VERSION_10_12
#define MAC_OS_X_VERSION_10_12 101200 #define MAC_OS_X_VERSION_10_12 101200
#endif #endif
#ifndef MAC_OS_X_VERSION_10_13
#define MAC_OS_X_VERSION_10_13 101300
#endif
/* macOS 10.12 deprecated many constants, #define the new names for older SDKs */ /* macOS 10.12 deprecated many constants, #define the new names for older SDKs */
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
@ -90,6 +93,14 @@
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
#define NSModalResponseOK NSFileHandlingPanelOKButton #define NSModalResponseOK NSFileHandlingPanelOKButton
#endif #endif
/* 10.14 deprecates NSOnState and NSOffState in favor of
* NSControlStateValueOn/Off, which were introduced in 10.13.
* Define for older versions
*/
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
#define NSControlStateValueOn NSOnState
#define NSControlStateValueOff NSOffState
#endif
//#define DEBUG //#define DEBUG
@ -377,7 +388,12 @@ - (void) drawRect:(NSRect) rect
COCOA_DEBUG("QemuCocoaView: drawRect\n"); COCOA_DEBUG("QemuCocoaView: drawRect\n");
// get CoreGraphic context // get CoreGraphic context
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
#else
CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext];
#endif
CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
CGContextSetShouldAntialias (viewContextRef, NO); CGContextSetShouldAntialias (viewContextRef, NO);
@ -1147,9 +1163,9 @@ - (void)zoomToFit:(id) sender
{ {
stretch_video = !stretch_video; stretch_video = !stretch_video;
if (stretch_video == true) { if (stretch_video == true) {
[sender setState: NSOnState]; [sender setState: NSControlStateValueOn];
} else { } else {
[sender setState: NSOffState]; [sender setState: NSControlStateValueOff];
} }
} }
@ -1390,15 +1406,15 @@ - (void)adjustSpeed:(id)sender
{ {
/* Unselect the currently selected item */ /* Unselect the currently selected item */
for (NSMenuItem *item in [menu itemArray]) { for (NSMenuItem *item in [menu itemArray]) {
if (item.state == NSOnState) { if (item.state == NSControlStateValueOn) {
[item setState: NSOffState]; [item setState: NSControlStateValueOff];
break; break;
} }
} }
} }
// check the menu item // check the menu item
[sender setState: NSOnState]; [sender setState: NSControlStateValueOn];
// get the throttle percentage // get the throttle percentage
throttle_pct = [sender tag]; throttle_pct = [sender tag];
@ -1502,7 +1518,7 @@ int main (int argc, const char * argv[]) {
initWithTitle: [NSString stringWithFormat: @"%d%%", percentage] action:@selector(adjustSpeed:) keyEquivalent:@""] autorelease]; initWithTitle: [NSString stringWithFormat: @"%d%%", percentage] action:@selector(adjustSpeed:) keyEquivalent:@""] autorelease];
if (percentage == 100) { if (percentage == 100) {
[menuItem setState: NSOnState]; [menuItem setState: NSControlStateValueOn];
} }
/* Calculate the throttle percentage */ /* Calculate the throttle percentage */

View File

@ -273,7 +273,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
} }
keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK, keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
false, false, false); NULL, false);
if (keycode == 0) if (keycode == 0)
continue; continue;

View File

@ -142,7 +142,8 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
!edpy->y_0_top); !edpy->y_0_top);
egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb,
!edpy->y_0_top, edpy->pos_x, edpy->pos_y); !edpy->y_0_top, edpy->pos_x, edpy->pos_y,
1.0, 1.0);
} else { } else {
/* no cursor -> use simple framebuffer blit */ /* no cursor -> use simple framebuffer blit */
egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);

View File

@ -120,14 +120,15 @@ void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip)
} }
void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip,
int x, int y) int x, int y, double scale_x, double scale_y)
{ {
glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
int w = scale_x * src->width;
int h = scale_y * src->height;
if (flip) { if (flip) {
glViewport(x, y, src->width, src->height); glViewport(x, y, w, h);
} else { } else {
glViewport(x, dst->height - src->height - y, glViewport(x, dst->height - h - y, w, h);
src->width, src->height);
} }
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, src->texture); glBindTexture(GL_TEXTURE_2D, src->texture);

View File

@ -278,7 +278,8 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
vc->gfx.y0_top); vc->gfx.y0_top);
egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb, egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
vc->gfx.y0_top, vc->gfx.y0_top,
vc->gfx.cursor_x, vc->gfx.cursor_y); vc->gfx.cursor_x, vc->gfx.cursor_y,
vc->gfx.scale_x, vc->gfx.scale_y);
} else { } else {
egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top); egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
} }

View File

@ -122,17 +122,6 @@
#define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
static const int modifier_keycode[] = {
Q_KEY_CODE_SHIFT,
Q_KEY_CODE_SHIFT_R,
Q_KEY_CODE_CTRL,
Q_KEY_CODE_CTRL_R,
Q_KEY_CODE_ALT,
Q_KEY_CODE_ALT_R,
Q_KEY_CODE_META_L,
Q_KEY_CODE_META_R,
};
static const guint16 *keycode_map; static const guint16 *keycode_map;
static size_t keycode_maplen; static size_t keycode_maplen;
@ -187,7 +176,6 @@ struct GtkDisplayState {
bool external_pause_update; bool external_pause_update;
bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
bool ignore_keys; bool ignore_keys;
DisplayOptions *opts; DisplayOptions *opts;
@ -426,20 +414,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
static void gtk_release_modifiers(GtkDisplayState *s) static void gtk_release_modifiers(GtkDisplayState *s)
{ {
VirtualConsole *vc = gd_vc_find_current(s); VirtualConsole *vc = gd_vc_find_current(s);
int i, qcode;
if (vc->type != GD_VC_GFX || if (vc->type != GD_VC_GFX ||
!qemu_console_is_graphic(vc->gfx.dcl.con)) { !qemu_console_is_graphic(vc->gfx.dcl.con)) {
return; return;
} }
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { qkbd_state_lift_all_keys(vc->gfx.kbd);
qcode = modifier_keycode[i];
if (!s->modifier_pressed[i]) {
continue;
}
qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false);
s->modifier_pressed[i] = false;
}
} }
static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
@ -1004,7 +984,9 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
&delta_x, &delta_y)) { &delta_x, &delta_y)) {
return TRUE; return TRUE;
} }
if (delta_y > 0) { if (delta_y == 0) {
return TRUE;
} else if (delta_y > 0) {
btn = INPUT_BUTTON_WHEEL_DOWN; btn = INPUT_BUTTON_WHEEL_DOWN;
} else { } else {
btn = INPUT_BUTTON_WHEEL_UP; btn = INPUT_BUTTON_WHEEL_UP;
@ -1113,7 +1095,6 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
VirtualConsole *vc = opaque; VirtualConsole *vc = opaque;
GtkDisplayState *s = vc->s; GtkDisplayState *s = vc->s;
int qcode; int qcode;
int i;
if (s->ignore_keys) { if (s->ignore_keys) {
s->ignore_keys = (key->type == GDK_KEY_PRESS); s->ignore_keys = (key->type == GDK_KEY_PRESS);
@ -1134,8 +1115,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
|| key->hardware_keycode == VK_PAUSE || key->hardware_keycode == VK_PAUSE
#endif #endif
) { ) {
qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
key->type == GDK_KEY_PRESS); key->type == GDK_KEY_PRESS);
return TRUE; return TRUE;
} }
@ -1144,14 +1125,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
trace_gd_key_event(vc->label, key->hardware_keycode, qcode, trace_gd_key_event(vc->label, key->hardware_keycode, qcode,
(key->type == GDK_KEY_PRESS) ? "down" : "up"); (key->type == GDK_KEY_PRESS) ? "down" : "up");
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { qkbd_state_key_event(vc->gfx.kbd, qcode,
if (qcode == modifier_keycode[i]) { key->type == GDK_KEY_PRESS);
s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
}
}
qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode,
key->type == GDK_KEY_PRESS);
return TRUE; return TRUE;
} }
@ -2043,6 +2018,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
GDK_ENTER_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_SCROLL_MASK | GDK_SCROLL_MASK |
GDK_SMOOTH_SCROLL_MASK |
GDK_KEY_PRESS_MASK); GDK_KEY_PRESS_MASK);
gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
@ -2052,6 +2028,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
vc->tab_item, gtk_label_new(vc->label)); vc->tab_item, gtk_label_new(vc->label));
vc->gfx.kbd = qkbd_state_init(con);
vc->gfx.dcl.con = con; vc->gfx.dcl.con = con;
register_displaychangelistener(&vc->gfx.dcl); register_displaychangelistener(&vc->gfx.dcl);

130
ui/kbd-state.c Normal file
View File

@ -0,0 +1,130 @@
/*
* This work is licensed under the terms of the GNU GPL, version 2 or
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/bitmap.h"
#include "qemu/queue.h"
#include "ui/console.h"
#include "ui/input.h"
#include "ui/kbd-state.h"
struct QKbdState {
QemuConsole *con;
int key_delay_ms;
DECLARE_BITMAP(keys, Q_KEY_CODE__MAX);
DECLARE_BITMAP(mods, QKBD_MOD__MAX);
};
static void qkbd_state_modifier_update(QKbdState *kbd,
QKeyCode qcode1, QKeyCode qcode2,
QKbdModifier mod)
{
if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) {
set_bit(mod, kbd->mods);
} else {
clear_bit(mod, kbd->mods);
}
}
bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod)
{
return test_bit(mod, kbd->mods);
}
bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode)
{
return test_bit(qcode, kbd->keys);
}
void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down)
{
bool state = test_bit(qcode, kbd->keys);
if (state == down) {
/*
* Filter out events which don't change the keyboard state.
*
* Most notably this allows to simply send along all key-up
* events, and this function will filter out everything where
* the corresponding key-down event wasn't send to the guest,
* for example due to being a host hotkey.
*/
return;
}
/* update key and modifier state */
change_bit(qcode, kbd->keys);
switch (qcode) {
case Q_KEY_CODE_SHIFT:
case Q_KEY_CODE_SHIFT_R:
qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R,
QKBD_MOD_SHIFT);
break;
case Q_KEY_CODE_CTRL:
case Q_KEY_CODE_CTRL_R:
qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R,
QKBD_MOD_CTRL);
break;
case Q_KEY_CODE_ALT:
qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT,
QKBD_MOD_ALT);
break;
case Q_KEY_CODE_ALT_R:
qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R,
QKBD_MOD_ALTGR);
break;
case Q_KEY_CODE_CAPS_LOCK:
if (down) {
change_bit(QKBD_MOD_CAPSLOCK, kbd->mods);
}
break;
case Q_KEY_CODE_NUM_LOCK:
if (down) {
change_bit(QKBD_MOD_NUMLOCK, kbd->mods);
}
break;
default:
/* keep gcc happy */
break;
}
/* send to guest */
if (qemu_console_is_graphic(kbd->con)) {
qemu_input_event_send_key_qcode(kbd->con, qcode, down);
if (kbd->key_delay_ms) {
qemu_input_event_send_key_delay(kbd->key_delay_ms);
}
}
}
void qkbd_state_lift_all_keys(QKbdState *kbd)
{
int qcode;
for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) {
if (test_bit(qcode, kbd->keys)) {
qkbd_state_key_event(kbd, qcode, false);
}
}
}
void qkbd_state_set_delay(QKbdState *kbd, int delay_ms)
{
kbd->key_delay_ms = delay_ms;
}
void qkbd_state_free(QKbdState *kbd)
{
g_free(kbd);
}
QKbdState *qkbd_state_init(QemuConsole *con)
{
QKbdState *kbd = g_new0(QKbdState, 1);
kbd->con = con;
return kbd;
}

View File

@ -28,6 +28,7 @@
#include "trace.h" #include "trace.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "ui/input.h"
struct keysym2code { struct keysym2code {
uint32_t count; uint32_t count;
@ -188,7 +189,7 @@ kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
int keysym2scancode(kbd_layout_t *k, int keysym, int keysym2scancode(kbd_layout_t *k, int keysym,
bool shift, bool altgr, bool ctrl) QKbdState *kbd, bool down)
{ {
static const uint32_t mask = static const uint32_t mask =
SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL; SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
@ -212,27 +213,39 @@ int keysym2scancode(kbd_layout_t *k, int keysym,
return keysym2code->keycodes[0]; return keysym2code->keycodes[0];
} }
/* /* We have multiple keysym -> keycode mappings. */
* We have multiple keysym -> keycode mappings. if (down) {
* /*
* Check whenever we find one mapping where the modifier state of * On keydown: Check whenever we find one mapping where the
* the mapping matches the current user interface modifier state. * modifier state of the mapping matches the current user
* If so, prefer that one. * interface modifier state. If so, prefer that one.
*/ */
mods = 0; mods = 0;
if (shift) { if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
mods |= SCANCODE_SHIFT; mods |= SCANCODE_SHIFT;
} }
if (altgr) { if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
mods |= SCANCODE_ALTGR; mods |= SCANCODE_ALTGR;
} }
if (ctrl) { if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) {
mods |= SCANCODE_CTRL; mods |= SCANCODE_CTRL;
} }
for (i = 0; i < keysym2code->count; i++) { for (i = 0; i < keysym2code->count; i++) {
if ((keysym2code->keycodes[i] & mask) == mods) { if ((keysym2code->keycodes[i] & mask) == mods) {
return keysym2code->keycodes[i]; return keysym2code->keycodes[i];
}
}
} else {
/*
* On keyup: Try find a key which is actually down.
*/
for (i = 0; i < keysym2code->count; i++) {
QKeyCode qcode = qemu_input_key_number_to_qcode
(keysym2code->keycodes[i]);
if (kbd && qkbd_state_key_get(kbd, qcode)) {
return keysym2code->keycodes[i];
}
} }
} }
return keysym2code->keycodes[0]; return keysym2code->keycodes[0];

View File

@ -26,6 +26,7 @@
#define QEMU_KEYMAPS_H #define QEMU_KEYMAPS_H
#include "qemu-common.h" #include "qemu-common.h"
#include "ui/kbd-state.h"
typedef struct { typedef struct {
const char* name; const char* name;
@ -55,7 +56,7 @@ typedef struct kbd_layout_t kbd_layout_t;
kbd_layout_t *init_keyboard_layout(const name2keysym_t *table, kbd_layout_t *init_keyboard_layout(const name2keysym_t *table,
const char *language, Error **errp); const char *language, Error **errp);
int keysym2scancode(kbd_layout_t *k, int keysym, int keysym2scancode(kbd_layout_t *k, int keysym,
bool shift, bool altgr, bool ctrl); QKbdState *kbd, bool down);
int keycode_is_keypad(kbd_layout_t *k, int keycode); int keycode_is_keypad(kbd_layout_t *k, int keycode);
int keysym_is_numlock(kbd_layout_t *k, int keysym); int keysym_is_numlock(kbd_layout_t *k, int keysym);

View File

@ -30,63 +30,23 @@
#include "ui/sdl2.h" #include "ui/sdl2.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
static uint8_t modifiers_state[SDL_NUM_SCANCODES];
void sdl2_reset_keys(struct sdl2_console *scon)
{
QemuConsole *con = scon ? scon->dcl.con : NULL;
int i;
for (i = 0 ;
i < SDL_NUM_SCANCODES && i < qemu_input_map_usb_to_qcode_len ;
i++) {
if (modifiers_state[i]) {
int qcode = qemu_input_map_usb_to_qcode[i];
qemu_input_event_send_key_qcode(con, qcode, false);
modifiers_state[i] = 0;
}
}
}
void sdl2_process_key(struct sdl2_console *scon, void sdl2_process_key(struct sdl2_console *scon,
SDL_KeyboardEvent *ev) SDL_KeyboardEvent *ev)
{ {
int qcode; int qcode;
QemuConsole *con = scon ? scon->dcl.con : NULL; QemuConsole *con = scon->dcl.con;
if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) { if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) {
return; return;
} }
qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode];
qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN);
/* modifier state tracking */
switch (ev->keysym.scancode) {
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;
}
break;
default:
/* nothing */
break;
}
if (!qemu_console_is_graphic(con)) { if (!qemu_console_is_graphic(con)) {
bool ctrl = (modifiers_state[SDL_SCANCODE_LCTRL] || bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
modifiers_state[SDL_SCANCODE_RCTRL]);
if (ev->type == SDL_KEYDOWN) { if (ev->type == SDL_KEYDOWN) {
switch (ev->keysym.scancode) { switch (qcode) {
case SDL_SCANCODE_RETURN: case Q_KEY_CODE_RET:
kbd_put_keysym_console(con, '\n'); kbd_put_keysym_console(con, '\n');
break; break;
default: default:

View File

@ -38,7 +38,6 @@ static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
static int gui_saved_grab; static int gui_saved_grab;
static int gui_fullscreen; static int gui_fullscreen;
static int gui_keysym;
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_normal;
static SDL_Cursor *sdl_cursor_hidden; static SDL_Cursor *sdl_cursor_hidden;
@ -330,6 +329,7 @@ static void handle_keydown(SDL_Event *ev)
int win; int win;
struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
int gui_key_modifier_pressed = get_mod_state(); int gui_key_modifier_pressed = get_mod_state();
int gui_keysym = 0;
if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) {
switch (ev->key.keysym.scancode) { switch (ev->key.keysym.scancode) {
@ -410,16 +410,9 @@ static void handle_keydown(SDL_Event *ev)
static void handle_keyup(SDL_Event *ev) static void handle_keyup(SDL_Event *ev)
{ {
struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
int gui_key_modifier_pressed = get_mod_state();
scon->ignore_hotkeys = false; scon->ignore_hotkeys = false;
sdl2_process_key(scon, &ev->key);
if (!gui_key_modifier_pressed) {
gui_keysym = 0;
}
if (!gui_keysym) {
sdl2_process_key(scon, &ev->key);
}
} }
static void handle_textinput(SDL_Event *ev) static void handle_textinput(SDL_Event *ev)
@ -823,6 +816,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
sdl2_console[i].dcl.ops = &dcl_2d_ops; sdl2_console[i].dcl.ops = &dcl_2d_ops;
#endif #endif
sdl2_console[i].dcl.con = con; sdl2_console[i].dcl.con = con;
sdl2_console[i].kbd = qkbd_state_init(con);
register_displaychangelistener(&sdl2_console[i].dcl); register_displaychangelistener(&sdl2_console[i].dcl);
#if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11) #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)

View File

@ -1,278 +0,0 @@
#include "keymaps.h"
static const name2keysym_t name2keysym[]={
/* ascii */
{ "space", 0x020},
{ "exclam", 0x021},
{ "quotedbl", 0x022},
{ "numbersign", 0x023},
{ "dollar", 0x024},
{ "percent", 0x025},
{ "ampersand", 0x026},
{ "apostrophe", 0x027},
{ "parenleft", 0x028},
{ "parenright", 0x029},
{ "asterisk", 0x02a},
{ "plus", 0x02b},
{ "comma", 0x02c},
{ "minus", 0x02d},
{ "period", 0x02e},
{ "slash", 0x02f},
{ "0", 0x030},
{ "1", 0x031},
{ "2", 0x032},
{ "3", 0x033},
{ "4", 0x034},
{ "5", 0x035},
{ "6", 0x036},
{ "7", 0x037},
{ "8", 0x038},
{ "9", 0x039},
{ "colon", 0x03a},
{ "semicolon", 0x03b},
{ "less", 0x03c},
{ "equal", 0x03d},
{ "greater", 0x03e},
{ "question", 0x03f},
{ "at", 0x040},
{ "A", 0x041},
{ "B", 0x042},
{ "C", 0x043},
{ "D", 0x044},
{ "E", 0x045},
{ "F", 0x046},
{ "G", 0x047},
{ "H", 0x048},
{ "I", 0x049},
{ "J", 0x04a},
{ "K", 0x04b},
{ "L", 0x04c},
{ "M", 0x04d},
{ "N", 0x04e},
{ "O", 0x04f},
{ "P", 0x050},
{ "Q", 0x051},
{ "R", 0x052},
{ "S", 0x053},
{ "T", 0x054},
{ "U", 0x055},
{ "V", 0x056},
{ "W", 0x057},
{ "X", 0x058},
{ "Y", 0x059},
{ "Z", 0x05a},
{ "bracketleft", 0x05b},
{ "backslash", 0x05c},
{ "bracketright", 0x05d},
{ "asciicircum", 0x05e},
{ "underscore", 0x05f},
{ "grave", 0x060},
{ "a", 0x061},
{ "b", 0x062},
{ "c", 0x063},
{ "d", 0x064},
{ "e", 0x065},
{ "f", 0x066},
{ "g", 0x067},
{ "h", 0x068},
{ "i", 0x069},
{ "j", 0x06a},
{ "k", 0x06b},
{ "l", 0x06c},
{ "m", 0x06d},
{ "n", 0x06e},
{ "o", 0x06f},
{ "p", 0x070},
{ "q", 0x071},
{ "r", 0x072},
{ "s", 0x073},
{ "t", 0x074},
{ "u", 0x075},
{ "v", 0x076},
{ "w", 0x077},
{ "x", 0x078},
{ "y", 0x079},
{ "z", 0x07a},
{ "braceleft", 0x07b},
{ "bar", 0x07c},
{ "braceright", 0x07d},
{ "asciitilde", 0x07e},
/* latin 1 extensions */
{ "nobreakspace", 0x0a0},
{ "exclamdown", 0x0a1},
{ "cent", 0x0a2},
{ "sterling", 0x0a3},
{ "currency", 0x0a4},
{ "yen", 0x0a5},
{ "brokenbar", 0x0a6},
{ "section", 0x0a7},
{ "diaeresis", 0x0a8},
{ "copyright", 0x0a9},
{ "ordfeminine", 0x0aa},
{ "guillemotleft", 0x0ab},
{ "notsign", 0x0ac},
{ "hyphen", 0x0ad},
{ "registered", 0x0ae},
{ "macron", 0x0af},
{ "degree", 0x0b0},
{ "plusminus", 0x0b1},
{ "twosuperior", 0x0b2},
{ "threesuperior", 0x0b3},
{ "acute", 0x0b4},
{ "mu", 0x0b5},
{ "paragraph", 0x0b6},
{ "periodcentered", 0x0b7},
{ "cedilla", 0x0b8},
{ "onesuperior", 0x0b9},
{ "masculine", 0x0ba},
{ "guillemotright", 0x0bb},
{ "onequarter", 0x0bc},
{ "onehalf", 0x0bd},
{ "threequarters", 0x0be},
{ "questiondown", 0x0bf},
{ "Agrave", 0x0c0},
{ "Aacute", 0x0c1},
{ "Acircumflex", 0x0c2},
{ "Atilde", 0x0c3},
{ "Adiaeresis", 0x0c4},
{ "Aring", 0x0c5},
{ "AE", 0x0c6},
{ "Ccedilla", 0x0c7},
{ "Egrave", 0x0c8},
{ "Eacute", 0x0c9},
{ "Ecircumflex", 0x0ca},
{ "Ediaeresis", 0x0cb},
{ "Igrave", 0x0cc},
{ "Iacute", 0x0cd},
{ "Icircumflex", 0x0ce},
{ "Idiaeresis", 0x0cf},
{ "ETH", 0x0d0},
{ "Eth", 0x0d0},
{ "Ntilde", 0x0d1},
{ "Ograve", 0x0d2},
{ "Oacute", 0x0d3},
{ "Ocircumflex", 0x0d4},
{ "Otilde", 0x0d5},
{ "Odiaeresis", 0x0d6},
{ "multiply", 0x0d7},
{ "Ooblique", 0x0d8},
{ "Oslash", 0x0d8},
{ "Ugrave", 0x0d9},
{ "Uacute", 0x0da},
{ "Ucircumflex", 0x0db},
{ "Udiaeresis", 0x0dc},
{ "Yacute", 0x0dd},
{ "THORN", 0x0de},
{ "Thorn", 0x0de},
{ "ssharp", 0x0df},
{ "agrave", 0x0e0},
{ "aacute", 0x0e1},
{ "acircumflex", 0x0e2},
{ "atilde", 0x0e3},
{ "adiaeresis", 0x0e4},
{ "aring", 0x0e5},
{ "ae", 0x0e6},
{ "ccedilla", 0x0e7},
{ "egrave", 0x0e8},
{ "eacute", 0x0e9},
{ "ecircumflex", 0x0ea},
{ "ediaeresis", 0x0eb},
{ "igrave", 0x0ec},
{ "iacute", 0x0ed},
{ "icircumflex", 0x0ee},
{ "idiaeresis", 0x0ef},
{ "eth", 0x0f0},
{ "ntilde", 0x0f1},
{ "ograve", 0x0f2},
{ "oacute", 0x0f3},
{ "ocircumflex", 0x0f4},
{ "otilde", 0x0f5},
{ "odiaeresis", 0x0f6},
{ "division", 0x0f7},
{ "oslash", 0x0f8},
{ "ooblique", 0x0f8},
{ "ugrave", 0x0f9},
{ "uacute", 0x0fa},
{ "ucircumflex", 0x0fb},
{ "udiaeresis", 0x0fc},
{ "yacute", 0x0fd},
{ "thorn", 0x0fe},
{ "ydiaeresis", 0x0ff},
#if SDL_MAJOR_VERSION == 1
{"EuroSign", SDLK_EURO},
/* modifiers */
{"Control_L", SDLK_LCTRL},
{"Control_R", SDLK_RCTRL},
{"Alt_L", SDLK_LALT},
{"Alt_R", SDLK_RALT},
{"Caps_Lock", SDLK_CAPSLOCK},
{"Meta_L", SDLK_LMETA},
{"Meta_R", SDLK_RMETA},
{"Shift_L", SDLK_LSHIFT},
{"Shift_R", SDLK_RSHIFT},
{"Super_L", SDLK_LSUPER},
{"Super_R", SDLK_RSUPER},
/* special keys */
{"BackSpace", SDLK_BACKSPACE},
{"Tab", SDLK_TAB},
{"Return", SDLK_RETURN},
{"Right", SDLK_RIGHT},
{"Left", SDLK_LEFT},
{"Up", SDLK_UP},
{"Down", SDLK_DOWN},
{"Page_Down", SDLK_PAGEDOWN},
{"Page_Up", SDLK_PAGEUP},
{"Insert", SDLK_INSERT},
{"Delete", SDLK_DELETE},
{"Home", SDLK_HOME},
{"End", SDLK_END},
{"Scroll_Lock", SDLK_SCROLLOCK},
{"F1", SDLK_F1},
{"F2", SDLK_F2},
{"F3", SDLK_F3},
{"F4", SDLK_F4},
{"F5", SDLK_F5},
{"F6", SDLK_F6},
{"F7", SDLK_F7},
{"F8", SDLK_F8},
{"F9", SDLK_F9},
{"F10", SDLK_F10},
{"F11", SDLK_F11},
{"F12", SDLK_F12},
{"F13", SDLK_F13},
{"F14", SDLK_F14},
{"F15", SDLK_F15},
{"Sys_Req", SDLK_SYSREQ},
{"KP_0", SDLK_KP0},
{"KP_1", SDLK_KP1},
{"KP_2", SDLK_KP2},
{"KP_3", SDLK_KP3},
{"KP_4", SDLK_KP4},
{"KP_5", SDLK_KP5},
{"KP_6", SDLK_KP6},
{"KP_7", SDLK_KP7},
{"KP_8", SDLK_KP8},
{"KP_9", SDLK_KP9},
{"KP_Add", SDLK_KP_PLUS},
{"KP_Decimal", SDLK_KP_PERIOD},
{"KP_Divide", SDLK_KP_DIVIDE},
{"KP_Enter", SDLK_KP_ENTER},
{"KP_Equal", SDLK_KP_EQUALS},
{"KP_Multiply", SDLK_KP_MULTIPLY},
{"KP_Subtract", SDLK_KP_MINUS},
{"help", SDLK_HELP},
{"Menu", SDLK_MENU},
{"Power", SDLK_POWER},
{"Print", SDLK_PRINT},
{"Mode_switch", SDLK_MODE},
{"Multi_Key", SDLK_COMPOSE},
{"Num_Lock", SDLK_NUMLOCK},
{"Pause", SDLK_PAUSE},
{"Escape", SDLK_ESCAPE},
#endif
{NULL, 0},
};

View File

@ -1090,7 +1090,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
!y_0_top); !y_0_top);
egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb,
!y_0_top, x, y); !y_0_top, x, y, 1.0, 1.0);
glFlush(); glFlush();
} }

119
ui/vnc.c
View File

@ -59,7 +59,6 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
QTAILQ_HEAD_INITIALIZER(vnc_displays); QTAILQ_HEAD_INITIALIZER(vnc_displays);
static int vnc_cursor_define(VncState *vs); static int vnc_cursor_define(VncState *vs);
static void vnc_release_modifiers(VncState *vs);
static void vnc_update_throttle_offset(VncState *vs); static void vnc_update_throttle_offset(VncState *vs);
static void vnc_set_share_mode(VncState *vs, VncShareMode mode) static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
@ -1267,7 +1266,7 @@ void vnc_disconnect_finish(VncState *vs)
vnc_sasl_client_cleanup(vs); vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */ #endif /* CONFIG_VNC_SASL */
audio_del(vs); audio_del(vs);
vnc_release_modifiers(vs); qkbd_state_lift_all_keys(vs->vd->kbd);
if (vs->mouse_mode_notifier.notify != NULL) { if (vs->mouse_mode_notifier.notify != NULL) {
qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
@ -1756,26 +1755,10 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y)
qemu_input_event_sync(); qemu_input_event_sync();
} }
static void reset_keys(VncState *vs) static void press_key(VncState *vs, QKeyCode qcode)
{ {
int i; qkbd_state_key_event(vs->vd->kbd, qcode, true);
for(i = 0; i < 256; i++) { qkbd_state_key_event(vs->vd->kbd, qcode, false);
if (vs->modifiers_state[i]) {
qemu_input_event_send_key_number(vs->vd->dcl.con, i, false);
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
vs->modifiers_state[i] = 0;
}
}
}
static void press_key(VncState *vs, int keysym)
{
int keycode = keysym2scancode(vs->vd->kbd_layout, keysym,
false, false, false) & SCANCODE_KEYMASK;
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
} }
static void vnc_led_state_change(VncState *vs) static void vnc_led_state_change(VncState *vs)
@ -1816,32 +1799,20 @@ static void kbd_leds(void *opaque, int ledstate)
static void do_key_event(VncState *vs, int down, int keycode, int sym) static void do_key_event(VncState *vs, int down, int keycode, int sym)
{ {
QKeyCode qcode = qemu_input_key_number_to_qcode(keycode);
/* QEMU console switch */ /* QEMU console switch */
switch(keycode) { switch (qcode) {
case 0x2a: /* Left Shift */ case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */
case 0x36: /* Right Shift */ if (vs->vd->dcl.con == NULL && down &&
case 0x1d: /* Left CTRL */ qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) &&
case 0x9d: /* Right CTRL */ qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
case 0x38: /* Left ALT */
case 0xb8: /* Right ALT */
if (down)
vs->modifiers_state[keycode] = 1;
else
vs->modifiers_state[keycode] = 0;
break;
case 0x02 ... 0x0a: /* '1' to '9' keys */
if (vs->vd->dcl.con == NULL &&
down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
/* Reset the modifiers sent to the current console */ /* Reset the modifiers sent to the current console */
reset_keys(vs); qkbd_state_lift_all_keys(vs->vd->kbd);
console_select(keycode - 0x02); console_select(qcode - Q_KEY_CODE_1);
return; return;
} }
break; default:
case 0x3a: /* CapsLock */
case 0x45: /* NumLock */
if (down)
vs->modifiers_state[keycode] ^= 1;
break; break;
} }
@ -1856,16 +1827,14 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
toggles numlock away from the VNC window. toggles numlock away from the VNC window.
*/ */
if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
if (!vs->modifiers_state[0x45]) { if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
trace_vnc_key_sync_numlock(true); trace_vnc_key_sync_numlock(true);
vs->modifiers_state[0x45] = 1; press_key(vs, Q_KEY_CODE_NUM_LOCK);
press_key(vs, 0xff7f);
} }
} else { } else {
if (vs->modifiers_state[0x45]) { if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
trace_vnc_key_sync_numlock(false); trace_vnc_key_sync_numlock(false);
vs->modifiers_state[0x45] = 0; press_key(vs, Q_KEY_CODE_NUM_LOCK);
press_key(vs, 0xff7f);
} }
} }
} }
@ -1878,30 +1847,25 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
toggles capslock away from the VNC window. toggles capslock away from the VNC window.
*/ */
int uppercase = !!(sym >= 'A' && sym <= 'Z'); int uppercase = !!(sym >= 'A' && sym <= 'Z');
int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]); bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT);
int capslock = !!(vs->modifiers_state[0x3a]); bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK);
if (capslock) { if (capslock) {
if (uppercase == shift) { if (uppercase == shift) {
trace_vnc_key_sync_capslock(false); trace_vnc_key_sync_capslock(false);
vs->modifiers_state[0x3a] = 0; press_key(vs, Q_KEY_CODE_CAPS_LOCK);
press_key(vs, 0xffe5);
} }
} else { } else {
if (uppercase != shift) { if (uppercase != shift) {
trace_vnc_key_sync_capslock(true); trace_vnc_key_sync_capslock(true);
vs->modifiers_state[0x3a] = 1; press_key(vs, Q_KEY_CODE_CAPS_LOCK);
press_key(vs, 0xffe5);
} }
} }
} }
if (qemu_console_is_graphic(NULL)) { qkbd_state_key_event(vs->vd->kbd, qcode, down);
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); if (!qemu_console_is_graphic(NULL)) {
qemu_input_event_send_key_delay(vs->vd->key_delay_ms); bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK);
} else { bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL);
bool numlock = vs->modifiers_state[0x45];
bool control = (vs->modifiers_state[0x1d] ||
vs->modifiers_state[0x9d]);
/* QEMU console emulation */ /* QEMU console emulation */
if (down) { if (down) {
switch (keycode) { switch (keycode) {
@ -2002,27 +1966,6 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
} }
} }
static void vnc_release_modifiers(VncState *vs)
{
static const int keycodes[] = {
/* shift, control, alt keys, both left & right */
0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8,
};
int i, keycode;
if (!qemu_console_is_graphic(NULL)) {
return;
}
for (i = 0; i < ARRAY_SIZE(keycodes); i++) {
keycode = keycodes[i];
if (!vs->modifiers_state[keycode]) {
continue;
}
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
}
}
static const char *code2name(int keycode) static const char *code2name(int keycode)
{ {
return QKeyCode_str(qemu_input_key_number_to_qcode(keycode)); return QKeyCode_str(qemu_input_key_number_to_qcode(keycode));
@ -2030,9 +1973,6 @@ static const char *code2name(int keycode)
static void key_event(VncState *vs, int down, uint32_t sym) static void key_event(VncState *vs, int down, uint32_t sym)
{ {
bool shift = vs->modifiers_state[0x2a] || vs->modifiers_state[0x36];
bool altgr = vs->modifiers_state[0xb8];
bool ctrl = vs->modifiers_state[0x1d] || vs->modifiers_state[0x9d];
int keycode; int keycode;
int lsym = sym; int lsym = sym;
@ -2041,7 +1981,7 @@ static void key_event(VncState *vs, int down, uint32_t sym)
} }
keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF, keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF,
shift, altgr, ctrl) & SCANCODE_KEYMASK; vs->vd->kbd, down) & SCANCODE_KEYMASK;
trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
do_key_event(vs, down, keycode, sym); do_key_event(vs, down, keycode, sym);
} }
@ -3259,6 +3199,7 @@ void vnc_display_init(const char *id, Error **errp)
vd->dcl.ops = &dcl_ops; vd->dcl.ops = &dcl_ops;
register_displaychangelistener(&vd->dcl); register_displaychangelistener(&vd->dcl);
vd->kbd = qkbd_state_init(vd->dcl.con);
} }
@ -3995,7 +3936,6 @@ void vnc_display_open(const char *id, Error **errp)
vd->led = qemu_add_led_event_handler(kbd_leds, vd); vd->led = qemu_add_led_event_handler(kbd_leds, vd);
} }
vd->ledstate = 0; vd->ledstate = 0;
vd->key_delay_ms = key_delay_ms;
device_id = qemu_opt_get(opts, "display"); device_id = qemu_opt_get(opts, "display");
if (device_id) { if (device_id) {
@ -4012,10 +3952,13 @@ void vnc_display_open(const char *id, Error **errp)
} }
if (con != vd->dcl.con) { if (con != vd->dcl.con) {
qkbd_state_free(vd->kbd);
unregister_displaychangelistener(&vd->dcl); unregister_displaychangelistener(&vd->dcl);
vd->dcl.con = con; vd->dcl.con = con;
register_displaychangelistener(&vd->dcl); register_displaychangelistener(&vd->dcl);
vd->kbd = qkbd_state_init(vd->dcl.con);
} }
qkbd_state_set_delay(vd->kbd, key_delay_ms);
if (saddr == NULL) { if (saddr == NULL) {
goto cleanup; goto cleanup;

View File

@ -44,6 +44,7 @@
#include "keymaps.h" #include "keymaps.h"
#include "vnc-palette.h" #include "vnc-palette.h"
#include "vnc-enc-zrle.h" #include "vnc-enc-zrle.h"
#include "ui/kbd-state.h"
// #define _VNC_DEBUG 1 // #define _VNC_DEBUG 1
@ -155,7 +156,7 @@ struct VncDisplay
int lock_key_sync; int lock_key_sync;
QEMUPutLEDEntry *led; QEMUPutLEDEntry *led;
int ledstate; int ledstate;
int key_delay_ms; QKbdState *kbd;
QemuMutex mutex; QemuMutex mutex;
QEMUCursor *cursor; QEMUCursor *cursor;
@ -326,8 +327,6 @@ struct VncState
VncReadEvent *read_handler; VncReadEvent *read_handler;
size_t read_handler_expect; size_t read_handler_expect;
/* input */
uint8_t modifiers_state[256];
bool abort; bool abort;
QemuMutex output_mutex; QemuMutex output_mutex;

23
vl.c
View File

@ -160,7 +160,6 @@ static int rtc_host_datetime_offset = -1; /* valid & used only with
QEMUClockType rtc_clock; QEMUClockType rtc_clock;
int vga_interface_type = VGA_NONE; int vga_interface_type = VGA_NONE;
static DisplayOptions dpy; static DisplayOptions dpy;
int no_frame;
static int num_serial_hds; static int num_serial_hds;
static Chardev **serial_hds; static Chardev **serial_hds;
Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *parallel_hds[MAX_PARALLEL_PORTS];
@ -2113,18 +2112,7 @@ static void parse_display(const char *p)
while (*opts) { while (*opts) {
const char *nextopt; const char *nextopt;
if (strstart(opts, ",frame=", &nextopt)) { if (strstart(opts, ",alt_grab=", &nextopt)) {
g_printerr("The frame= sdl option is deprecated, and will be\n"
"removed in a future release.\n");
opts = nextopt;
if (strstart(opts, "on", &nextopt)) {
no_frame = 0;
} else if (strstart(opts, "off", &nextopt)) {
no_frame = 1;
} else {
goto invalid_sdl_args;
}
} else if (strstart(opts, ",alt_grab=", &nextopt)) {
opts = nextopt; opts = nextopt;
if (strstart(opts, "on", &nextopt)) { if (strstart(opts, "on", &nextopt)) {
alt_grab = 1; alt_grab = 1;
@ -3596,11 +3584,6 @@ int main(int argc, char **argv, char **envp)
dpy.has_full_screen = true; dpy.has_full_screen = true;
dpy.full_screen = true; dpy.full_screen = true;
break; break;
case QEMU_OPTION_no_frame:
g_printerr("The -no-frame switch is deprecated, and will be\n"
"removed in a future release.\n");
no_frame = 1;
break;
case QEMU_OPTION_alt_grab: case QEMU_OPTION_alt_grab:
alt_grab = 1; alt_grab = 1;
break; break;
@ -4279,8 +4262,8 @@ int main(int argc, char **argv, char **envp)
dpy.type = DISPLAY_TYPE_NONE; dpy.type = DISPLAY_TYPE_NONE;
} }
if ((no_frame || alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) { if ((alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) {
error_report("-no-frame, -alt-grab and -ctrl-grab are only valid " error_report("-alt-grab and -ctrl-grab are only valid "
"for SDL, ignoring option"); "for SDL, ignoring option");
} }
if (dpy.has_window_close && if (dpy.has_window_close &&