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:
commit
01a9a51ffa
@ -102,7 +102,6 @@ extern const char *keyboard_layout;
|
||||
extern int win2k_install_hack;
|
||||
extern int alt_grab;
|
||||
extern int ctrl_grab;
|
||||
extern int no_frame;
|
||||
extern int smp_cpus;
|
||||
extern unsigned int max_cpus;
|
||||
extern int cursor_hide;
|
||||
|
@ -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_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
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <gdk/gdkwayland.h>
|
||||
#endif
|
||||
|
||||
#include "ui/kbd-state.h"
|
||||
#if defined(CONFIG_OPENGL)
|
||||
#include "ui/egl-helpers.h"
|
||||
#include "ui/egl-context.h"
|
||||
@ -32,6 +33,7 @@ typedef struct GtkDisplayState GtkDisplayState;
|
||||
typedef struct VirtualGfxConsole {
|
||||
GtkWidget *drawing_area;
|
||||
DisplayChangeListener dcl;
|
||||
QKbdState *kbd;
|
||||
DisplaySurface *ds;
|
||||
pixman_image_t *convert;
|
||||
cairo_surface_t *surface;
|
||||
|
101
include/ui/kbd-state.h
Normal file
101
include/ui/kbd-state.h
Normal 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 */
|
@ -10,6 +10,7 @@
|
||||
# include <SDL_image.h>
|
||||
#endif
|
||||
|
||||
#include "ui/kbd-state.h"
|
||||
#ifdef CONFIG_OPENGL
|
||||
# include "ui/egl-helpers.h"
|
||||
#endif
|
||||
@ -30,6 +31,7 @@ struct sdl2_console {
|
||||
int idle_counter;
|
||||
int ignore_hotkeys;
|
||||
SDL_GLContext winctx;
|
||||
QKbdState *kbd;
|
||||
#ifdef CONFIG_OPENGL
|
||||
QemuGLShader *gls;
|
||||
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_poll_events(struct sdl2_console *scon);
|
||||
|
||||
void sdl2_reset_keys(struct sdl2_console *scon);
|
||||
void sdl2_process_key(struct sdl2_console *scon,
|
||||
SDL_KeyboardEvent *ev);
|
||||
|
||||
|
@ -37,12 +37,6 @@ would automatically enable USB support on the machine type.
|
||||
If using the new syntax, USB support must be explicitly
|
||||
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)
|
||||
|
||||
Option @option{-virtioconsole} has been replaced by
|
||||
|
@ -1294,17 +1294,6 @@ mode using a curses/ncurses interface. Nothing is displayed in graphical
|
||||
mode.
|
||||
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,
|
||||
"-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n",
|
||||
QEMU_ARCH_ALL)
|
||||
|
@ -8,7 +8,7 @@ vnc-obj-y += vnc-ws.o
|
||||
vnc-obj-y += vnc-jobs.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_SPICE) += spice-core.o spice-input.o spice-display.o
|
||||
common-obj-$(CONFIG_COCOA) += cocoa.o
|
||||
|
28
ui/cocoa.m
28
ui/cocoa.m
@ -54,6 +54,9 @@
|
||||
#ifndef MAC_OS_X_VERSION_10_12
|
||||
#define MAC_OS_X_VERSION_10_12 101200
|
||||
#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 */
|
||||
#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
|
||||
#define NSModalResponseOK NSFileHandlingPanelOKButton
|
||||
#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
|
||||
|
||||
@ -377,7 +388,12 @@ QemuCocoaView *cocoaView;
|
||||
COCOA_DEBUG("QemuCocoaView: drawRect\n");
|
||||
|
||||
// get CoreGraphic context
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
|
||||
CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
|
||||
#else
|
||||
CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext];
|
||||
#endif
|
||||
|
||||
CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
|
||||
CGContextSetShouldAntialias (viewContextRef, NO);
|
||||
|
||||
@ -1147,9 +1163,9 @@ QemuCocoaView *cocoaView;
|
||||
{
|
||||
stretch_video = !stretch_video;
|
||||
if (stretch_video == true) {
|
||||
[sender setState: NSOnState];
|
||||
[sender setState: NSControlStateValueOn];
|
||||
} else {
|
||||
[sender setState: NSOffState];
|
||||
[sender setState: NSControlStateValueOff];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1390,15 +1406,15 @@ QemuCocoaView *cocoaView;
|
||||
{
|
||||
/* Unselect the currently selected item */
|
||||
for (NSMenuItem *item in [menu itemArray]) {
|
||||
if (item.state == NSOnState) {
|
||||
[item setState: NSOffState];
|
||||
if (item.state == NSControlStateValueOn) {
|
||||
[item setState: NSControlStateValueOff];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check the menu item
|
||||
[sender setState: NSOnState];
|
||||
[sender setState: NSControlStateValueOn];
|
||||
|
||||
// get the throttle percentage
|
||||
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];
|
||||
|
||||
if (percentage == 100) {
|
||||
[menuItem setState: NSOnState];
|
||||
[menuItem setState: NSControlStateValueOn];
|
||||
}
|
||||
|
||||
/* Calculate the throttle percentage */
|
||||
|
@ -273,7 +273,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
|
||||
}
|
||||
|
||||
keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK,
|
||||
false, false, false);
|
||||
NULL, false);
|
||||
if (keycode == 0)
|
||||
continue;
|
||||
|
||||
|
@ -142,7 +142,8 @@ static void egl_scanout_flush(DisplayChangeListener *dcl,
|
||||
egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb,
|
||||
!edpy->y_0_top);
|
||||
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 {
|
||||
/* no cursor -> use simple framebuffer blit */
|
||||
egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top);
|
||||
|
@ -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,
|
||||
int x, int y)
|
||||
int x, int y, double scale_x, double scale_y)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer);
|
||||
int w = scale_x * src->width;
|
||||
int h = scale_y * src->height;
|
||||
if (flip) {
|
||||
glViewport(x, y, src->width, src->height);
|
||||
glViewport(x, y, w, h);
|
||||
} else {
|
||||
glViewport(x, dst->height - src->height - y,
|
||||
src->width, src->height);
|
||||
glViewport(x, dst->height - h - y, w, h);
|
||||
}
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, src->texture);
|
||||
|
@ -278,7 +278,8 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl,
|
||||
vc->gfx.y0_top);
|
||||
egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb,
|
||||
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 {
|
||||
egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top);
|
||||
}
|
||||
|
43
ui/gtk.c
43
ui/gtk.c
@ -122,17 +122,6 @@
|
||||
|
||||
#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 size_t keycode_maplen;
|
||||
|
||||
@ -187,7 +176,6 @@ struct GtkDisplayState {
|
||||
|
||||
bool external_pause_update;
|
||||
|
||||
bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
|
||||
bool ignore_keys;
|
||||
|
||||
DisplayOptions *opts;
|
||||
@ -426,20 +414,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
|
||||
static void gtk_release_modifiers(GtkDisplayState *s)
|
||||
{
|
||||
VirtualConsole *vc = gd_vc_find_current(s);
|
||||
int i, qcode;
|
||||
|
||||
if (vc->type != GD_VC_GFX ||
|
||||
!qemu_console_is_graphic(vc->gfx.dcl.con)) {
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
|
||||
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;
|
||||
}
|
||||
qkbd_state_lift_all_keys(vc->gfx.kbd);
|
||||
}
|
||||
|
||||
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)) {
|
||||
return TRUE;
|
||||
}
|
||||
if (delta_y > 0) {
|
||||
if (delta_y == 0) {
|
||||
return TRUE;
|
||||
} else if (delta_y > 0) {
|
||||
btn = INPUT_BUTTON_WHEEL_DOWN;
|
||||
} else {
|
||||
btn = INPUT_BUTTON_WHEEL_UP;
|
||||
@ -1113,7 +1095,6 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
|
||||
VirtualConsole *vc = opaque;
|
||||
GtkDisplayState *s = vc->s;
|
||||
int qcode;
|
||||
int i;
|
||||
|
||||
if (s->ignore_keys) {
|
||||
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
|
||||
#endif
|
||||
) {
|
||||
qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
|
||||
key->type == GDK_KEY_PRESS);
|
||||
qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE,
|
||||
key->type == GDK_KEY_PRESS);
|
||||
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,
|
||||
(key->type == GDK_KEY_PRESS) ? "down" : "up");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
|
||||
if (qcode == modifier_keycode[i]) {
|
||||
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);
|
||||
qkbd_state_key_event(vc->gfx.kbd, qcode,
|
||||
key->type == GDK_KEY_PRESS);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -2043,6 +2018,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
|
||||
GDK_ENTER_NOTIFY_MASK |
|
||||
GDK_LEAVE_NOTIFY_MASK |
|
||||
GDK_SCROLL_MASK |
|
||||
GDK_SMOOTH_SCROLL_MASK |
|
||||
GDK_KEY_PRESS_MASK);
|
||||
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),
|
||||
vc->tab_item, gtk_label_new(vc->label));
|
||||
|
||||
vc->gfx.kbd = qkbd_state_init(con);
|
||||
vc->gfx.dcl.con = con;
|
||||
register_displaychangelistener(&vc->gfx.dcl);
|
||||
|
||||
|
130
ui/kbd-state.c
Normal file
130
ui/kbd-state.c
Normal 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;
|
||||
}
|
55
ui/keymaps.c
55
ui/keymaps.c
@ -28,6 +28,7 @@
|
||||
#include "trace.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "ui/input.h"
|
||||
|
||||
struct keysym2code {
|
||||
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,
|
||||
bool shift, bool altgr, bool ctrl)
|
||||
QKbdState *kbd, bool down)
|
||||
{
|
||||
static const uint32_t mask =
|
||||
SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL;
|
||||
@ -212,27 +213,39 @@ int keysym2scancode(kbd_layout_t *k, int keysym,
|
||||
return keysym2code->keycodes[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* We have multiple keysym -> keycode mappings.
|
||||
*
|
||||
* Check whenever we find one mapping where the modifier state of
|
||||
* the mapping matches the current user interface modifier state.
|
||||
* If so, prefer that one.
|
||||
*/
|
||||
mods = 0;
|
||||
if (shift) {
|
||||
mods |= SCANCODE_SHIFT;
|
||||
}
|
||||
if (altgr) {
|
||||
mods |= SCANCODE_ALTGR;
|
||||
}
|
||||
if (ctrl) {
|
||||
mods |= SCANCODE_CTRL;
|
||||
}
|
||||
/* We have multiple keysym -> keycode mappings. */
|
||||
if (down) {
|
||||
/*
|
||||
* On keydown: Check whenever we find one mapping where the
|
||||
* modifier state of the mapping matches the current user
|
||||
* interface modifier state. If so, prefer that one.
|
||||
*/
|
||||
mods = 0;
|
||||
if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) {
|
||||
mods |= SCANCODE_SHIFT;
|
||||
}
|
||||
if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) {
|
||||
mods |= SCANCODE_ALTGR;
|
||||
}
|
||||
if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) {
|
||||
mods |= SCANCODE_CTRL;
|
||||
}
|
||||
|
||||
for (i = 0; i < keysym2code->count; i++) {
|
||||
if ((keysym2code->keycodes[i] & mask) == mods) {
|
||||
return keysym2code->keycodes[i];
|
||||
for (i = 0; i < keysym2code->count; i++) {
|
||||
if ((keysym2code->keycodes[i] & mask) == mods) {
|
||||
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];
|
||||
|
@ -26,6 +26,7 @@
|
||||
#define QEMU_KEYMAPS_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "ui/kbd-state.h"
|
||||
|
||||
typedef struct {
|
||||
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,
|
||||
const char *language, Error **errp);
|
||||
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 keysym_is_numlock(kbd_layout_t *k, int keysym);
|
||||
|
||||
|
@ -30,63 +30,23 @@
|
||||
#include "ui/sdl2.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,
|
||||
SDL_KeyboardEvent *ev)
|
||||
{
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode];
|
||||
|
||||
/* 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;
|
||||
}
|
||||
qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN);
|
||||
|
||||
if (!qemu_console_is_graphic(con)) {
|
||||
bool ctrl = (modifiers_state[SDL_SCANCODE_LCTRL] ||
|
||||
modifiers_state[SDL_SCANCODE_RCTRL]);
|
||||
bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL);
|
||||
if (ev->type == SDL_KEYDOWN) {
|
||||
switch (ev->keysym.scancode) {
|
||||
case SDL_SCANCODE_RETURN:
|
||||
switch (qcode) {
|
||||
case Q_KEY_CODE_RET:
|
||||
kbd_put_keysym_console(con, '\n');
|
||||
break;
|
||||
default:
|
||||
|
12
ui/sdl2.c
12
ui/sdl2.c
@ -38,7 +38,6 @@ static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
|
||||
|
||||
static int gui_saved_grab;
|
||||
static int gui_fullscreen;
|
||||
static int gui_keysym;
|
||||
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
|
||||
static SDL_Cursor *sdl_cursor_normal;
|
||||
static SDL_Cursor *sdl_cursor_hidden;
|
||||
@ -330,6 +329,7 @@ static void handle_keydown(SDL_Event *ev)
|
||||
int win;
|
||||
struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
|
||||
int gui_key_modifier_pressed = get_mod_state();
|
||||
int gui_keysym = 0;
|
||||
|
||||
if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) {
|
||||
switch (ev->key.keysym.scancode) {
|
||||
@ -410,16 +410,9 @@ static void handle_keydown(SDL_Event *ev)
|
||||
static void handle_keyup(SDL_Event *ev)
|
||||
{
|
||||
struct sdl2_console *scon = get_scon_from_window(ev->key.windowID);
|
||||
int gui_key_modifier_pressed = get_mod_state();
|
||||
|
||||
scon->ignore_hotkeys = false;
|
||||
|
||||
if (!gui_key_modifier_pressed) {
|
||||
gui_keysym = 0;
|
||||
}
|
||||
if (!gui_keysym) {
|
||||
sdl2_process_key(scon, &ev->key);
|
||||
}
|
||||
sdl2_process_key(scon, &ev->key);
|
||||
}
|
||||
|
||||
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;
|
||||
#endif
|
||||
sdl2_console[i].dcl.con = con;
|
||||
sdl2_console[i].kbd = qkbd_state_init(con);
|
||||
register_displaychangelistener(&sdl2_console[i].dcl);
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11)
|
||||
|
278
ui/sdl_keysym.h
278
ui/sdl_keysym.h
@ -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},
|
||||
};
|
@ -1090,7 +1090,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl,
|
||||
egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb,
|
||||
!y_0_top);
|
||||
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();
|
||||
}
|
||||
|
||||
|
119
ui/vnc.c
119
ui/vnc.c
@ -59,7 +59,6 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays =
|
||||
QTAILQ_HEAD_INITIALIZER(vnc_displays);
|
||||
|
||||
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_set_share_mode(VncState *vs, VncShareMode mode)
|
||||
@ -1267,7 +1266,7 @@ void vnc_disconnect_finish(VncState *vs)
|
||||
vnc_sasl_client_cleanup(vs);
|
||||
#endif /* CONFIG_VNC_SASL */
|
||||
audio_del(vs);
|
||||
vnc_release_modifiers(vs);
|
||||
qkbd_state_lift_all_keys(vs->vd->kbd);
|
||||
|
||||
if (vs->mouse_mode_notifier.notify != NULL) {
|
||||
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();
|
||||
}
|
||||
|
||||
static void reset_keys(VncState *vs)
|
||||
static void press_key(VncState *vs, QKeyCode qcode)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 256; i++) {
|
||||
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);
|
||||
qkbd_state_key_event(vs->vd->kbd, qcode, true);
|
||||
qkbd_state_key_event(vs->vd->kbd, qcode, false);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
QKeyCode qcode = qemu_input_key_number_to_qcode(keycode);
|
||||
|
||||
/* QEMU console switch */
|
||||
switch(keycode) {
|
||||
case 0x2a: /* Left Shift */
|
||||
case 0x36: /* Right Shift */
|
||||
case 0x1d: /* Left CTRL */
|
||||
case 0x9d: /* Right CTRL */
|
||||
case 0x38: /* Left ALT */
|
||||
case 0xb8: /* Right ALT */
|
||||
if (down)
|
||||
vs->modifiers_state[keycode] = 1;
|
||||
else
|
||||
vs->modifiers_state[keycode] = 0;
|
||||
break;
|
||||
case 0x02 ... 0x0a: /* '1' to '9' keys */
|
||||
if (vs->vd->dcl.con == NULL &&
|
||||
down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
|
||||
switch (qcode) {
|
||||
case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */
|
||||
if (vs->vd->dcl.con == NULL && down &&
|
||||
qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) &&
|
||||
qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) {
|
||||
/* Reset the modifiers sent to the current console */
|
||||
reset_keys(vs);
|
||||
console_select(keycode - 0x02);
|
||||
qkbd_state_lift_all_keys(vs->vd->kbd);
|
||||
console_select(qcode - Q_KEY_CODE_1);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x3a: /* CapsLock */
|
||||
case 0x45: /* NumLock */
|
||||
if (down)
|
||||
vs->modifiers_state[keycode] ^= 1;
|
||||
default:
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
vs->modifiers_state[0x45] = 1;
|
||||
press_key(vs, 0xff7f);
|
||||
press_key(vs, Q_KEY_CODE_NUM_LOCK);
|
||||
}
|
||||
} else {
|
||||
if (vs->modifiers_state[0x45]) {
|
||||
if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) {
|
||||
trace_vnc_key_sync_numlock(false);
|
||||
vs->modifiers_state[0x45] = 0;
|
||||
press_key(vs, 0xff7f);
|
||||
press_key(vs, Q_KEY_CODE_NUM_LOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1878,30 +1847,25 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
|
||||
toggles capslock away from the VNC window.
|
||||
*/
|
||||
int uppercase = !!(sym >= 'A' && sym <= 'Z');
|
||||
int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]);
|
||||
int capslock = !!(vs->modifiers_state[0x3a]);
|
||||
bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT);
|
||||
bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK);
|
||||
if (capslock) {
|
||||
if (uppercase == shift) {
|
||||
trace_vnc_key_sync_capslock(false);
|
||||
vs->modifiers_state[0x3a] = 0;
|
||||
press_key(vs, 0xffe5);
|
||||
press_key(vs, Q_KEY_CODE_CAPS_LOCK);
|
||||
}
|
||||
} else {
|
||||
if (uppercase != shift) {
|
||||
trace_vnc_key_sync_capslock(true);
|
||||
vs->modifiers_state[0x3a] = 1;
|
||||
press_key(vs, 0xffe5);
|
||||
press_key(vs, Q_KEY_CODE_CAPS_LOCK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_console_is_graphic(NULL)) {
|
||||
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down);
|
||||
qemu_input_event_send_key_delay(vs->vd->key_delay_ms);
|
||||
} else {
|
||||
bool numlock = vs->modifiers_state[0x45];
|
||||
bool control = (vs->modifiers_state[0x1d] ||
|
||||
vs->modifiers_state[0x9d]);
|
||||
qkbd_state_key_event(vs->vd->kbd, qcode, down);
|
||||
if (!qemu_console_is_graphic(NULL)) {
|
||||
bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK);
|
||||
bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL);
|
||||
/* QEMU console emulation */
|
||||
if (down) {
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 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,
|
||||
shift, altgr, ctrl) & SCANCODE_KEYMASK;
|
||||
vs->vd->kbd, down) & SCANCODE_KEYMASK;
|
||||
trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
|
||||
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;
|
||||
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->ledstate = 0;
|
||||
vd->key_delay_ms = key_delay_ms;
|
||||
|
||||
device_id = qemu_opt_get(opts, "display");
|
||||
if (device_id) {
|
||||
@ -4012,10 +3952,13 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
}
|
||||
|
||||
if (con != vd->dcl.con) {
|
||||
qkbd_state_free(vd->kbd);
|
||||
unregister_displaychangelistener(&vd->dcl);
|
||||
vd->dcl.con = con;
|
||||
register_displaychangelistener(&vd->dcl);
|
||||
vd->kbd = qkbd_state_init(vd->dcl.con);
|
||||
}
|
||||
qkbd_state_set_delay(vd->kbd, key_delay_ms);
|
||||
|
||||
if (saddr == NULL) {
|
||||
goto cleanup;
|
||||
|
5
ui/vnc.h
5
ui/vnc.h
@ -44,6 +44,7 @@
|
||||
#include "keymaps.h"
|
||||
#include "vnc-palette.h"
|
||||
#include "vnc-enc-zrle.h"
|
||||
#include "ui/kbd-state.h"
|
||||
|
||||
// #define _VNC_DEBUG 1
|
||||
|
||||
@ -155,7 +156,7 @@ struct VncDisplay
|
||||
int lock_key_sync;
|
||||
QEMUPutLEDEntry *led;
|
||||
int ledstate;
|
||||
int key_delay_ms;
|
||||
QKbdState *kbd;
|
||||
QemuMutex mutex;
|
||||
|
||||
QEMUCursor *cursor;
|
||||
@ -326,8 +327,6 @@ struct VncState
|
||||
|
||||
VncReadEvent *read_handler;
|
||||
size_t read_handler_expect;
|
||||
/* input */
|
||||
uint8_t modifiers_state[256];
|
||||
|
||||
bool abort;
|
||||
QemuMutex output_mutex;
|
||||
|
23
vl.c
23
vl.c
@ -160,7 +160,6 @@ static int rtc_host_datetime_offset = -1; /* valid & used only with
|
||||
QEMUClockType rtc_clock;
|
||||
int vga_interface_type = VGA_NONE;
|
||||
static DisplayOptions dpy;
|
||||
int no_frame;
|
||||
static int num_serial_hds;
|
||||
static Chardev **serial_hds;
|
||||
Chardev *parallel_hds[MAX_PARALLEL_PORTS];
|
||||
@ -2113,18 +2112,7 @@ static void parse_display(const char *p)
|
||||
while (*opts) {
|
||||
const char *nextopt;
|
||||
|
||||
if (strstart(opts, ",frame=", &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)) {
|
||||
if (strstart(opts, ",alt_grab=", &nextopt)) {
|
||||
opts = nextopt;
|
||||
if (strstart(opts, "on", &nextopt)) {
|
||||
alt_grab = 1;
|
||||
@ -3596,11 +3584,6 @@ int main(int argc, char **argv, char **envp)
|
||||
dpy.has_full_screen = true;
|
||||
dpy.full_screen = true;
|
||||
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:
|
||||
alt_grab = 1;
|
||||
break;
|
||||
@ -4279,8 +4262,8 @@ int main(int argc, char **argv, char **envp)
|
||||
dpy.type = DISPLAY_TYPE_NONE;
|
||||
}
|
||||
|
||||
if ((no_frame || alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) {
|
||||
error_report("-no-frame, -alt-grab and -ctrl-grab are only valid "
|
||||
if ((alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) {
|
||||
error_report("-alt-grab and -ctrl-grab are only valid "
|
||||
"for SDL, ignoring option");
|
||||
}
|
||||
if (dpy.has_window_close &&
|
||||
|
Loading…
Reference in New Issue
Block a user