summaryrefslogtreecommitdiffstats
path: root/qemu/ui
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/ui')
-rw-r--r--qemu/ui/Makefile.objs9
-rw-r--r--qemu/ui/cocoa.m500
-rw-r--r--qemu/ui/console-gl.c8
-rw-r--r--qemu/ui/console.c310
-rw-r--r--qemu/ui/curses.c65
-rw-r--r--qemu/ui/curses_keys.h8
-rw-r--r--qemu/ui/cursor.c1
-rw-r--r--qemu/ui/egl-context.c35
-rw-r--r--qemu/ui/egl-helpers.c137
-rw-r--r--qemu/ui/gtk-egl.c132
-rw-r--r--qemu/ui/gtk-gl-area.c224
-rw-r--r--qemu/ui/gtk.c286
-rw-r--r--qemu/ui/input-keymap.c25
-rw-r--r--qemu/ui/input-legacy.c43
-rw-r--r--qemu/ui/input-linux.c507
-rw-r--r--qemu/ui/input.c158
-rw-r--r--qemu/ui/keymaps.c3
-rw-r--r--qemu/ui/qemu-pixman.c1
-rw-r--r--qemu/ui/sdl.c28
-rw-r--r--qemu/ui/sdl2-2d.c14
-rw-r--r--qemu/ui/sdl2-gl.c136
-rw-r--r--qemu/ui/sdl2-input.c1
-rw-r--r--qemu/ui/sdl2.c33
-rw-r--r--qemu/ui/sdl_zoom.c4
-rw-r--r--qemu/ui/shader.c32
-rw-r--r--qemu/ui/spice-core.c53
-rw-r--r--qemu/ui/spice-display.c170
-rw-r--r--qemu/ui/spice-input.c7
-rw-r--r--qemu/ui/vnc-auth-sasl.c96
-rw-r--r--qemu/ui/vnc-auth-vencrypt.c93
-rw-r--r--qemu/ui/vnc-enc-hextile.c1
-rw-r--r--qemu/ui/vnc-enc-tight.c3
-rw-r--r--qemu/ui/vnc-enc-zlib.c1
-rw-r--r--qemu/ui/vnc-enc-zrle-template.c2
-rw-r--r--qemu/ui/vnc-enc-zrle.c1
-rw-r--r--qemu/ui/vnc-enc-zywrle-template.c1
-rw-r--r--qemu/ui/vnc-jobs.c53
-rw-r--r--qemu/ui/vnc-palette.c2
-rw-r--r--qemu/ui/vnc-palette.h2
-rw-r--r--qemu/ui/vnc-tls.c474
-rw-r--r--qemu/ui/vnc-tls.h69
-rw-r--r--qemu/ui/vnc-ws.c390
-rw-r--r--qemu/ui/vnc-ws.h73
-rw-r--r--qemu/ui/vnc.c1220
-rw-r--r--qemu/ui/vnc.h61
-rw-r--r--qemu/ui/x_keymap.c1
46 files changed, 3260 insertions, 2213 deletions
diff --git a/qemu/ui/Makefile.objs b/qemu/ui/Makefile.objs
index c62d4d972..dc936f150 100644
--- a/qemu/ui/Makefile.objs
+++ b/qemu/ui/Makefile.objs
@@ -2,13 +2,14 @@ vnc-obj-y += vnc.o
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o
-vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
+vnc-obj-y += vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
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-$(CONFIG_LINUX) += input-linux.o
common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
common-obj-$(CONFIG_SDL) += sdl.mo x_keymap.o
common-obj-$(CONFIG_COCOA) += cocoa.o
@@ -31,11 +32,17 @@ ifeq ($(CONFIG_OPENGL),y)
common-obj-y += shader.o
common-obj-y += console-gl.o
common-obj-y += egl-helpers.o
+common-obj-y += egl-context.o
+ifeq ($(CONFIG_GTK_GL),y)
+common-obj-$(CONFIG_GTK) += gtk-gl-area.o
+else
common-obj-$(CONFIG_GTK) += gtk-egl.o
endif
+endif
gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
+gtk-gl-area.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
shader.o-cflags += $(OPENGL_CFLAGS)
console-gl.o-cflags += $(OPENGL_CFLAGS)
egl-helpers.o-cflags += $(OPENGL_CFLAGS)
diff --git a/qemu/ui/cocoa.m b/qemu/ui/cocoa.m
index 334e6f666..60a7c07ec 100644
--- a/qemu/ui/cocoa.m
+++ b/qemu/ui/cocoa.m
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+
#import <Cocoa/Cocoa.h>
#include <crt_externs.h>
@@ -31,6 +33,7 @@
#include "sysemu/sysemu.h"
#include "qmp-commands.h"
#include "sysemu/blockdev.h"
+#include <Carbon/Carbon.h>
#ifndef MAC_OS_X_VERSION_10_5
#define MAC_OS_X_VERSION_10_5 1050
@@ -70,178 +73,139 @@ bool stretch_video;
NSTextField *pauseLabel;
NSArray * supportedImageFileTypes;
-// keymap conversion
-int keymap[] =
-{
-// SdlI macI macH SdlH 104xtH 104xtC sdl
- 30, // 0 0x00 0x1e A QZ_a
- 31, // 1 0x01 0x1f S QZ_s
- 32, // 2 0x02 0x20 D QZ_d
- 33, // 3 0x03 0x21 F QZ_f
- 35, // 4 0x04 0x23 H QZ_h
- 34, // 5 0x05 0x22 G QZ_g
- 44, // 6 0x06 0x2c Z QZ_z
- 45, // 7 0x07 0x2d X QZ_x
- 46, // 8 0x08 0x2e C QZ_c
- 47, // 9 0x09 0x2f V QZ_v
- 0, // 10 0x0A Undefined
- 48, // 11 0x0B 0x30 B QZ_b
- 16, // 12 0x0C 0x10 Q QZ_q
- 17, // 13 0x0D 0x11 W QZ_w
- 18, // 14 0x0E 0x12 E QZ_e
- 19, // 15 0x0F 0x13 R QZ_r
- 21, // 16 0x10 0x15 Y QZ_y
- 20, // 17 0x11 0x14 T QZ_t
- 2, // 18 0x12 0x02 1 QZ_1
- 3, // 19 0x13 0x03 2 QZ_2
- 4, // 20 0x14 0x04 3 QZ_3
- 5, // 21 0x15 0x05 4 QZ_4
- 7, // 22 0x16 0x07 6 QZ_6
- 6, // 23 0x17 0x06 5 QZ_5
- 13, // 24 0x18 0x0d = QZ_EQUALS
- 10, // 25 0x19 0x0a 9 QZ_9
- 8, // 26 0x1A 0x08 7 QZ_7
- 12, // 27 0x1B 0x0c - QZ_MINUS
- 9, // 28 0x1C 0x09 8 QZ_8
- 11, // 29 0x1D 0x0b 0 QZ_0
- 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET
- 24, // 31 0x1F 0x18 O QZ_o
- 22, // 32 0x20 0x16 U QZ_u
- 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET
- 23, // 34 0x22 0x17 I QZ_i
- 25, // 35 0x23 0x19 P QZ_p
- 28, // 36 0x24 0x1c ENTER QZ_RETURN
- 38, // 37 0x25 0x26 L QZ_l
- 36, // 38 0x26 0x24 J QZ_j
- 40, // 39 0x27 0x28 ' QZ_QUOTE
- 37, // 40 0x28 0x25 K QZ_k
- 39, // 41 0x29 0x27 ; QZ_SEMICOLON
- 43, // 42 0x2A 0x2b \ QZ_BACKSLASH
- 51, // 43 0x2B 0x33 , QZ_COMMA
- 53, // 44 0x2C 0x35 / QZ_SLASH
- 49, // 45 0x2D 0x31 N QZ_n
- 50, // 46 0x2E 0x32 M QZ_m
- 52, // 47 0x2F 0x34 . QZ_PERIOD
- 15, // 48 0x30 0x0f TAB QZ_TAB
- 57, // 49 0x31 0x39 SPACE QZ_SPACE
- 41, // 50 0x32 0x29 ` QZ_BACKQUOTE
- 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE
- 0, // 52 0x34 Undefined
- 1, // 53 0x35 0x01 ESC QZ_ESCAPE
- 220, // 54 0x36 0xdc E0,5C R GUI QZ_RMETA
- 219, // 55 0x37 0xdb E0,5B L GUI QZ_LMETA
- 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT
- 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK
- 56, // 58 0x3A 0x38 L ALT QZ_LALT
- 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL
- 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT
- 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT
- 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL
- 0, // 63 0x3F Undefined
- 0, // 64 0x40 Undefined
- 0, // 65 0x41 Undefined
- 0, // 66 0x42 Undefined
- 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY
- 0, // 68 0x44 Undefined
- 78, // 69 0x45 0x4e KP + QZ_KP_PLUS
- 0, // 70 0x46 Undefined
- 69, // 71 0x47 0x45 NUM QZ_NUMLOCK
- 0, // 72 0x48 Undefined
- 0, // 73 0x49 Undefined
- 0, // 74 0x4A Undefined
- 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE
- 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER
- 0, // 77 0x4D undefined
- 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS
- 0, // 79 0x4F Undefined
- 0, // 80 0x50 Undefined
- 0, // 81 0x51 QZ_KP_EQUALS
- 82, // 82 0x52 0x52 KP 0 QZ_KP0
- 79, // 83 0x53 0x4f KP 1 QZ_KP1
- 80, // 84 0x54 0x50 KP 2 QZ_KP2
- 81, // 85 0x55 0x51 KP 3 QZ_KP3
- 75, // 86 0x56 0x4b KP 4 QZ_KP4
- 76, // 87 0x57 0x4c KP 5 QZ_KP5
- 77, // 88 0x58 0x4d KP 6 QZ_KP6
- 71, // 89 0x59 0x47 KP 7 QZ_KP7
- 0, // 90 0x5A Undefined
- 72, // 91 0x5B 0x48 KP 8 QZ_KP8
- 73, // 92 0x5C 0x49 KP 9 QZ_KP9
- 0, // 93 0x5D Undefined
- 0, // 94 0x5E Undefined
- 0, // 95 0x5F Undefined
- 63, // 96 0x60 0x3f F5 QZ_F5
- 64, // 97 0x61 0x40 F6 QZ_F6
- 65, // 98 0x62 0x41 F7 QZ_F7
- 61, // 99 0x63 0x3d F3 QZ_F3
- 66, // 100 0x64 0x42 F8 QZ_F8
- 67, // 101 0x65 0x43 F9 QZ_F9
- 0, // 102 0x66 Undefined
- 87, // 103 0x67 0x57 F11 QZ_F11
- 0, // 104 0x68 Undefined
- 183,// 105 0x69 0xb7 QZ_PRINT
- 0, // 106 0x6A Undefined
- 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK
- 0, // 108 0x6C Undefined
- 68, // 109 0x6D 0x44 F10 QZ_F10
- 0, // 110 0x6E Undefined
- 88, // 111 0x6F 0x58 F12 QZ_F12
- 0, // 112 0x70 Undefined
- 110,// 113 0x71 0x0 QZ_PAUSE
- 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT
- 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME
- 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP
- 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE
- 62, // 118 0x76 0x3e F4 QZ_F4
- 207,// 119 0x77 0xcf E0,4f END QZ_END
- 60, // 120 0x78 0x3c F2 QZ_F2
- 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN
- 59, // 122 0x7A 0x3b F1 QZ_F1
- 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT
- 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT
- 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN
- 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP
-/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */
-
-/* Additional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
-/*
- 221 // 0xdd e0,5d APPS
- // E0,2A,E0,37 PRNT SCRN
- // E1,1D,45,E1,9D,C5 PAUSE
- 83 // 0x53 0x53 KP .
-// ACPI Scan Codes
- 222 // 0xde E0, 5E Power
- 223 // 0xdf E0, 5F Sleep
- 227 // 0xe3 E0, 63 Wake
-// Windows Multimedia Scan Codes
- 153 // 0x99 E0, 19 Next Track
- 144 // 0x90 E0, 10 Previous Track
- 164 // 0xa4 E0, 24 Stop
- 162 // 0xa2 E0, 22 Play/Pause
- 160 // 0xa0 E0, 20 Mute
- 176 // 0xb0 E0, 30 Volume Up
- 174 // 0xae E0, 2E Volume Down
- 237 // 0xed E0, 6D Media Select
- 236 // 0xec E0, 6C E-Mail
- 161 // 0xa1 E0, 21 Calculator
- 235 // 0xeb E0, 6B My Computer
- 229 // 0xe5 E0, 65 WWW Search
- 178 // 0xb2 E0, 32 WWW Home
- 234 // 0xea E0, 6A WWW Back
- 233 // 0xe9 E0, 69 WWW Forward
- 232 // 0xe8 E0, 68 WWW Stop
- 231 // 0xe7 E0, 67 WWW Refresh
- 230 // 0xe6 E0, 66 WWW Favorites
-*/
+// Mac to QKeyCode conversion
+const int mac_to_qkeycode_map[] = {
+ [kVK_ANSI_A] = Q_KEY_CODE_A,
+ [kVK_ANSI_B] = Q_KEY_CODE_B,
+ [kVK_ANSI_C] = Q_KEY_CODE_C,
+ [kVK_ANSI_D] = Q_KEY_CODE_D,
+ [kVK_ANSI_E] = Q_KEY_CODE_E,
+ [kVK_ANSI_F] = Q_KEY_CODE_F,
+ [kVK_ANSI_G] = Q_KEY_CODE_G,
+ [kVK_ANSI_H] = Q_KEY_CODE_H,
+ [kVK_ANSI_I] = Q_KEY_CODE_I,
+ [kVK_ANSI_J] = Q_KEY_CODE_J,
+ [kVK_ANSI_K] = Q_KEY_CODE_K,
+ [kVK_ANSI_L] = Q_KEY_CODE_L,
+ [kVK_ANSI_M] = Q_KEY_CODE_M,
+ [kVK_ANSI_N] = Q_KEY_CODE_N,
+ [kVK_ANSI_O] = Q_KEY_CODE_O,
+ [kVK_ANSI_P] = Q_KEY_CODE_P,
+ [kVK_ANSI_Q] = Q_KEY_CODE_Q,
+ [kVK_ANSI_R] = Q_KEY_CODE_R,
+ [kVK_ANSI_S] = Q_KEY_CODE_S,
+ [kVK_ANSI_T] = Q_KEY_CODE_T,
+ [kVK_ANSI_U] = Q_KEY_CODE_U,
+ [kVK_ANSI_V] = Q_KEY_CODE_V,
+ [kVK_ANSI_W] = Q_KEY_CODE_W,
+ [kVK_ANSI_X] = Q_KEY_CODE_X,
+ [kVK_ANSI_Y] = Q_KEY_CODE_Y,
+ [kVK_ANSI_Z] = Q_KEY_CODE_Z,
+
+ [kVK_ANSI_0] = Q_KEY_CODE_0,
+ [kVK_ANSI_1] = Q_KEY_CODE_1,
+ [kVK_ANSI_2] = Q_KEY_CODE_2,
+ [kVK_ANSI_3] = Q_KEY_CODE_3,
+ [kVK_ANSI_4] = Q_KEY_CODE_4,
+ [kVK_ANSI_5] = Q_KEY_CODE_5,
+ [kVK_ANSI_6] = Q_KEY_CODE_6,
+ [kVK_ANSI_7] = Q_KEY_CODE_7,
+ [kVK_ANSI_8] = Q_KEY_CODE_8,
+ [kVK_ANSI_9] = Q_KEY_CODE_9,
+
+ [kVK_ANSI_Grave] = Q_KEY_CODE_GRAVE_ACCENT,
+ [kVK_ANSI_Minus] = Q_KEY_CODE_MINUS,
+ [kVK_ANSI_Equal] = Q_KEY_CODE_EQUAL,
+ [kVK_Delete] = Q_KEY_CODE_BACKSPACE,
+ [kVK_CapsLock] = Q_KEY_CODE_CAPS_LOCK,
+ [kVK_Tab] = Q_KEY_CODE_TAB,
+ [kVK_Return] = Q_KEY_CODE_RET,
+ [kVK_ANSI_LeftBracket] = Q_KEY_CODE_BRACKET_LEFT,
+ [kVK_ANSI_RightBracket] = Q_KEY_CODE_BRACKET_RIGHT,
+ [kVK_ANSI_Backslash] = Q_KEY_CODE_BACKSLASH,
+ [kVK_ANSI_Semicolon] = Q_KEY_CODE_SEMICOLON,
+ [kVK_ANSI_Quote] = Q_KEY_CODE_APOSTROPHE,
+ [kVK_ANSI_Comma] = Q_KEY_CODE_COMMA,
+ [kVK_ANSI_Period] = Q_KEY_CODE_DOT,
+ [kVK_ANSI_Slash] = Q_KEY_CODE_SLASH,
+ [kVK_Shift] = Q_KEY_CODE_SHIFT,
+ [kVK_RightShift] = Q_KEY_CODE_SHIFT_R,
+ [kVK_Control] = Q_KEY_CODE_CTRL,
+ [kVK_RightControl] = Q_KEY_CODE_CTRL_R,
+ [kVK_Option] = Q_KEY_CODE_ALT,
+ [kVK_RightOption] = Q_KEY_CODE_ALT_R,
+ [kVK_Command] = Q_KEY_CODE_META_L,
+ [0x36] = Q_KEY_CODE_META_R, /* There is no kVK_RightCommand */
+ [kVK_Space] = Q_KEY_CODE_SPC,
+
+ [kVK_ANSI_Keypad0] = Q_KEY_CODE_KP_0,
+ [kVK_ANSI_Keypad1] = Q_KEY_CODE_KP_1,
+ [kVK_ANSI_Keypad2] = Q_KEY_CODE_KP_2,
+ [kVK_ANSI_Keypad3] = Q_KEY_CODE_KP_3,
+ [kVK_ANSI_Keypad4] = Q_KEY_CODE_KP_4,
+ [kVK_ANSI_Keypad5] = Q_KEY_CODE_KP_5,
+ [kVK_ANSI_Keypad6] = Q_KEY_CODE_KP_6,
+ [kVK_ANSI_Keypad7] = Q_KEY_CODE_KP_7,
+ [kVK_ANSI_Keypad8] = Q_KEY_CODE_KP_8,
+ [kVK_ANSI_Keypad9] = Q_KEY_CODE_KP_9,
+ [kVK_ANSI_KeypadDecimal] = Q_KEY_CODE_KP_DECIMAL,
+ [kVK_ANSI_KeypadEnter] = Q_KEY_CODE_KP_ENTER,
+ [kVK_ANSI_KeypadPlus] = Q_KEY_CODE_KP_ADD,
+ [kVK_ANSI_KeypadMinus] = Q_KEY_CODE_KP_SUBTRACT,
+ [kVK_ANSI_KeypadMultiply] = Q_KEY_CODE_KP_MULTIPLY,
+ [kVK_ANSI_KeypadDivide] = Q_KEY_CODE_KP_DIVIDE,
+ [kVK_ANSI_KeypadEquals] = Q_KEY_CODE_KP_EQUALS,
+ [kVK_ANSI_KeypadClear] = Q_KEY_CODE_NUM_LOCK,
+
+ [kVK_UpArrow] = Q_KEY_CODE_UP,
+ [kVK_DownArrow] = Q_KEY_CODE_DOWN,
+ [kVK_LeftArrow] = Q_KEY_CODE_LEFT,
+ [kVK_RightArrow] = Q_KEY_CODE_RIGHT,
+
+ [kVK_Help] = Q_KEY_CODE_INSERT,
+ [kVK_Home] = Q_KEY_CODE_HOME,
+ [kVK_PageUp] = Q_KEY_CODE_PGUP,
+ [kVK_PageDown] = Q_KEY_CODE_PGDN,
+ [kVK_End] = Q_KEY_CODE_END,
+ [kVK_ForwardDelete] = Q_KEY_CODE_DELETE,
+
+ [kVK_Escape] = Q_KEY_CODE_ESC,
+
+ /* The Power key can't be used directly because the operating system uses
+ * it. This key can be emulated by using it in place of another key such as
+ * F1. Don't forget to disable the real key binding.
+ */
+ /* [kVK_F1] = Q_KEY_CODE_POWER, */
+
+ [kVK_F1] = Q_KEY_CODE_F1,
+ [kVK_F2] = Q_KEY_CODE_F2,
+ [kVK_F3] = Q_KEY_CODE_F3,
+ [kVK_F4] = Q_KEY_CODE_F4,
+ [kVK_F5] = Q_KEY_CODE_F5,
+ [kVK_F6] = Q_KEY_CODE_F6,
+ [kVK_F7] = Q_KEY_CODE_F7,
+ [kVK_F8] = Q_KEY_CODE_F8,
+ [kVK_F9] = Q_KEY_CODE_F9,
+ [kVK_F10] = Q_KEY_CODE_F10,
+ [kVK_F11] = Q_KEY_CODE_F11,
+ [kVK_F12] = Q_KEY_CODE_F12,
+ [kVK_F13] = Q_KEY_CODE_PRINT,
+ [kVK_F14] = Q_KEY_CODE_SCROLL_LOCK,
+ [kVK_F15] = Q_KEY_CODE_PAUSE,
+
+ /*
+ * The eject and volume keys can't be used here because they are handled at
+ * a lower level than what an Application can see.
+ */
};
static int cocoa_keycode_to_qemu(int keycode)
{
- if (ARRAY_SIZE(keymap) <= keycode) {
+ if (ARRAY_SIZE(mac_to_qkeycode_map) <= keycode) {
fprintf(stderr, "(cocoa) warning unknown keycode 0x%x\n", keycode);
return 0;
}
- return keymap[keycode];
+ return mac_to_qkeycode_map[keycode];
}
/* Displays an alert dialog box with the specified message */
@@ -304,6 +268,7 @@ static void handleAnyDeviceErrors(Error * err)
- (float) cdx;
- (float) cdy;
- (QEMUScreen) gscreen;
+- (void) raiseAllKeys;
@end
QemuCocoaView *cocoaView;
@@ -554,21 +519,24 @@ QemuCocoaView *cocoaView;
case NSFlagsChanged:
keycode = cocoa_keycode_to_qemu([event keyCode]);
- if ((keycode == 219 || keycode == 220) && !isMouseGrabbed) {
+ if ((keycode == Q_KEY_CODE_META_L || keycode == Q_KEY_CODE_META_R)
+ && !isMouseGrabbed) {
/* Don't pass command key changes to guest unless mouse is grabbed */
keycode = 0;
}
if (keycode) {
- if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
- qemu_input_event_send_key_number(dcl->con, keycode, true);
- qemu_input_event_send_key_number(dcl->con, keycode, false);
+ // emulate caps lock and num lock keydown and keyup
+ if (keycode == Q_KEY_CODE_CAPS_LOCK ||
+ keycode == Q_KEY_CODE_NUM_LOCK) {
+ qemu_input_event_send_key_qcode(dcl->con, keycode, true);
+ qemu_input_event_send_key_qcode(dcl->con, keycode, false);
} else if (qemu_console_is_graphic(NULL)) {
if (modifiers_state[keycode] == 0) { // keydown
- qemu_input_event_send_key_number(dcl->con, keycode, true);
+ qemu_input_event_send_key_qcode(dcl->con, keycode, true);
modifiers_state[keycode] = 1;
} else { // keyup
- qemu_input_event_send_key_number(dcl->con, keycode, false);
+ qemu_input_event_send_key_qcode(dcl->con, keycode, false);
modifiers_state[keycode] = 0;
}
}
@@ -595,14 +563,14 @@ QemuCocoaView *cocoaView;
switch (keycode) {
// enable graphic console
- case 0x02 ... 0x0a: // '1' to '9' keys
- console_select(keycode - 0x02);
+ case Q_KEY_CODE_1 ... Q_KEY_CODE_9: // '1' to '9' keys
+ console_select(keycode - 11);
break;
}
// handle keys for graphic console
} else if (qemu_console_is_graphic(NULL)) {
- qemu_input_event_send_key_number(dcl->con, keycode, true);
+ qemu_input_event_send_key_qcode(dcl->con, keycode, true);
// handlekeys for Monitor
} else {
@@ -650,7 +618,7 @@ QemuCocoaView *cocoaView;
}
if (qemu_console_is_graphic(NULL)) {
- qemu_input_event_send_key_number(dcl->con, keycode, false);
+ qemu_input_event_send_key_qcode(dcl->con, keycode, false);
}
break;
case NSMouseMoved:
@@ -723,8 +691,16 @@ QemuCocoaView *cocoaView;
}
if (mouse_event) {
- if (last_buttons != buttons) {
- static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ /* Don't send button events to the guest unless we've got a
+ * mouse grab or window focus. If we have neither then this event
+ * is the user clicking on the background window to activate and
+ * bring us to the front, which will be done by the sendEvent
+ * call below. We definitely don't want to pass that click through
+ * to the guest.
+ */
+ if ((isMouseGrabbed || [[self window] isKeyWindow]) &&
+ (last_buttons != buttons)) {
+ static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
@@ -798,6 +774,24 @@ QemuCocoaView *cocoaView;
- (float) cdx {return cdx;}
- (float) cdy {return cdy;}
- (QEMUScreen) gscreen {return screen;}
+
+/*
+ * Makes the target think all down keys are being released.
+ * This prevents a stuck key problem, since we will not see
+ * key up events for those keys after we have lost focus.
+ */
+- (void) raiseAllKeys
+{
+ int index;
+ const int max_index = ARRAY_SIZE(modifiers_state);
+
+ for (index = 0; index < max_index; index++) {
+ if (modifiers_state[index]) {
+ modifiers_state[index] = 0;
+ qemu_input_event_send_key_qcode(dcl->con, index, false);
+ }
+ }
+}
@end
@@ -809,12 +803,11 @@ QemuCocoaView *cocoaView;
*/
@interface QemuCocoaAppController : NSObject
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
- <NSApplicationDelegate>
+ <NSWindowDelegate, NSApplicationDelegate>
#endif
{
}
- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
-- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;
- (void)doToggleFullScreen:(id)sender;
- (void)toggleFullScreen:(id)sender;
- (void)showQEMUDoc:(id)sender;
@@ -829,6 +822,8 @@ QemuCocoaView *cocoaView;
- (void)powerDownQEMU:(id)sender;
- (void)ejectDeviceMedia:(id)sender;
- (void)changeDeviceMedia:(id)sender;
+- (BOOL)verifyQuit;
+- (void)openDocumentation:(NSString *)filename;
@end
@implementation QemuCocoaAppController
@@ -855,13 +850,14 @@ QemuCocoaView *cocoaView;
exit(1);
}
[normalWindow setAcceptsMouseMovedEvents:YES];
- [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
+ [normalWindow setTitle:@"QEMU"];
[normalWindow setContentView:cocoaView];
#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10)
[normalWindow useOptimizedDrawing:YES];
#endif
[normalWindow makeKeyAndOrderFront:self];
[normalWindow center];
+ [normalWindow setDelegate: self];
stretch_video = false;
/* Used for displaying pause on the screen */
@@ -878,7 +874,8 @@ QemuCocoaView *cocoaView;
// set the supported image file types that can be opened
supportedImageFileTypes = [NSArray arrayWithObjects: @"img", @"iso", @"dmg",
- @"qcow", @"qcow2", @"cloop", @"vmdk", nil];
+ @"qcow", @"qcow2", @"cloop", @"vmdk", @"cdr",
+ nil];
}
return self;
}
@@ -895,29 +892,8 @@ QemuCocoaView *cocoaView;
- (void)applicationDidFinishLaunching: (NSNotification *) note
{
COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
-
- // Display an open dialog box if no arguments were passed or
- // if qemu was launched from the finder ( the Finder passes "-psn" )
- if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
- NSOpenPanel *op = [[NSOpenPanel alloc] init];
- [op setPrompt:@"Boot image"];
- [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
-#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
- [op setAllowedFileTypes:supportedImageFileTypes];
- [op beginSheetModalForWindow:normalWindow
- completionHandler:^(NSInteger returnCode)
- { [self openPanelDidEnd:op
- returnCode:returnCode contextInfo:NULL ]; } ];
-#else
- // Compatibility code for pre-10.6, using deprecated method
- [op beginSheetForDirectory:nil file:nil types:filetypes
- modalForWindow:normalWindow modalDelegate:self
- didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
-#endif
- } else {
- // or launch QEMU, with the global args
- [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
- }
+ // launch QEMU, with the global args
+ [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
@@ -933,43 +909,40 @@ QemuCocoaView *cocoaView;
return YES;
}
-- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+- (NSApplicationTerminateReply)applicationShouldTerminate:
+ (NSApplication *)sender
{
- COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
-
- int status;
- status = qemu_main(argc, argv, *_NSGetEnviron());
- exit(status);
+ COCOA_DEBUG("QemuCocoaAppController: applicationShouldTerminate\n");
+ return [self verifyQuit];
}
-- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+/* Called when the user clicks on a window's close button */
+- (BOOL)windowShouldClose:(id)sender
{
- COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
-
- /* The NSFileHandlingPanelOKButton/NSFileHandlingPanelCancelButton values for
- * returnCode strictly only apply for the 10.6-and-up beginSheetModalForWindow
- * API. For the legacy pre-10.6 beginSheetForDirectory API they are NSOKButton
- * and NSCancelButton. However conveniently the values are the same.
- * We use the non-legacy names because the others are deprecated in OSX 10.10.
+ COCOA_DEBUG("QemuCocoaAppController: windowShouldClose\n");
+ [NSApp terminate: sender];
+ /* If the user allows the application to quit then the call to
+ * NSApp terminate will never return. If we get here then the user
+ * cancelled the quit, so we should return NO to not permit the
+ * closing of this window.
*/
- if (returnCode == NSFileHandlingPanelCancelButton) {
- exit(0);
- } else if (returnCode == NSFileHandlingPanelOKButton) {
- char *img = (char*)[ [ [ sheet URL ] path ] cStringUsingEncoding:NSASCIIStringEncoding];
-
- char **argv = g_new(char *, 4);
-
- [sheet close];
+ return NO;
+}
- argv[0] = g_strdup(gArgv[0]);
- argv[1] = g_strdup("-hda");
- argv[2] = g_strdup(img);
- argv[3] = NULL;
+/* Called when QEMU goes into the background */
+- (void) applicationWillResignActive: (NSNotification *)aNotification
+{
+ COCOA_DEBUG("QemuCocoaAppController: applicationWillResignActive\n");
+ [cocoaView raiseAllKeys];
+}
- // printf("Using argc %d argv %s -hda %s\n", 3, gArgv[0], img);
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+{
+ COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
- [self startEmulationWithArgc:3 argv:(char**)argv];
- }
+ int status;
+ status = qemu_main(argc, argv, *_NSGetEnviron());
+ exit(status);
}
/* We abstract the method called by the Enter Fullscreen menu item
@@ -988,20 +961,42 @@ QemuCocoaView *cocoaView;
[cocoaView toggleFullScreen:sender];
}
+/* Tries to find then open the specified filename */
+- (void) openDocumentation: (NSString *) filename
+{
+ /* Where to look for local files */
+ NSString *path_array[] = {@"../share/doc/qemu/", @"../doc/qemu/", @"../"};
+ NSString *full_file_path;
+
+ /* iterate thru the possible paths until the file is found */
+ int index;
+ for (index = 0; index < ARRAY_SIZE(path_array); index++) {
+ full_file_path = [[NSBundle mainBundle] executablePath];
+ full_file_path = [full_file_path stringByDeletingLastPathComponent];
+ full_file_path = [NSString stringWithFormat: @"%@/%@%@", full_file_path,
+ path_array[index], filename];
+ if ([[NSWorkspace sharedWorkspace] openFile: full_file_path] == YES) {
+ return;
+ }
+ }
+
+ /* If none of the paths opened a file */
+ NSBeep();
+ QEMU_Alert(@"Failed to open file");
+}
+
- (void)showQEMUDoc:(id)sender
{
COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n");
- [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html",
- [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
+ [self openDocumentation: @"qemu-doc.html"];
}
- (void)showQEMUTec:(id)sender
{
COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n");
- [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
- [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
+ [self openDocumentation: @"qemu-tech.html"];
}
/* Stretches video to fit host monitor size */
@@ -1117,14 +1112,32 @@ QemuCocoaView *cocoaView;
}
Error *err = NULL;
- qmp_change_blockdev([drive cStringUsingEncoding: NSASCIIStringEncoding],
- [file cStringUsingEncoding: NSASCIIStringEncoding],
- "raw",
- &err);
+ qmp_blockdev_change_medium([drive cStringUsingEncoding:
+ NSASCIIStringEncoding],
+ [file cStringUsingEncoding:
+ NSASCIIStringEncoding],
+ true, "raw",
+ false, 0,
+ &err);
handleAnyDeviceErrors(err);
}
}
+/* Verifies if the user really wants to quit */
+- (BOOL)verifyQuit
+{
+ NSAlert *alert = [NSAlert new];
+ [alert autorelease];
+ [alert setMessageText: @"Are you sure you want to quit QEMU?"];
+ [alert addButtonWithTitle: @"Cancel"];
+ [alert addButtonWithTitle: @"Quit"];
+ if([alert runModal] == NSAlertSecondButtonReturn) {
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
@end
@@ -1148,6 +1161,7 @@ int main (int argc, const char * argv[]) {
!strcmp(opt, "-nographic") ||
!strcmp(opt, "-version") ||
!strcmp(opt, "-curses") ||
+ !strcmp(opt, "-display") ||
!strcmp(opt, "-qtest")) {
return qemu_main(gArgc, gArgv, *_NSGetEnviron());
}
@@ -1275,6 +1289,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
+ graphic_hw_update(NULL);
if (qemu_input_is_absolute()) {
if (![cocoaView isAbsoluteEnabled]) {
@@ -1295,7 +1310,6 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
[cocoaView handleEvent:event];
}
} while(event != nil);
- graphic_hw_update(NULL);
[pool release];
}
@@ -1341,7 +1355,7 @@ static void add_console_menu_entries(void)
/* Make menu items for all removable devices.
* Each device is given an 'Eject' and 'Change' menu item.
*/
-static void addRemovableDevicesMenuItems()
+static void addRemovableDevicesMenuItems(void)
{
NSMenu *menu;
NSMenuItem *menuItem;
diff --git a/qemu/ui/console-gl.c b/qemu/ui/console-gl.c
index cb45cf8a2..74b1bed6e 100644
--- a/qemu/ui/console-gl.c
+++ b/qemu/ui/console-gl.c
@@ -24,6 +24,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/shader.h"
@@ -33,6 +34,7 @@
struct ConsoleGLState {
GLint texture_blit_prog;
+ GLint texture_blit_vao;
};
/* ---------------------------------------------------------------------- */
@@ -47,6 +49,9 @@ ConsoleGLState *console_gl_init_context(void)
exit(1);
}
+ gls->texture_blit_vao =
+ qemu_gl_init_texture_blit(gls->texture_blit_prog);
+
return gls;
}
@@ -131,7 +136,8 @@ void surface_gl_render_texture(ConsoleGLState *gls,
glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
- qemu_gl_run_texture_blit(gls->texture_blit_prog);
+ qemu_gl_run_texture_blit(gls->texture_blit_prog,
+ gls->texture_blit_vao);
}
void surface_gl_destroy_texture(ConsoleGLState *gls,
diff --git a/qemu/ui/console.c b/qemu/ui/console.c
index 75fc492f7..bf385790b 100644
--- a/qemu/ui/console.c
+++ b/qemu/ui/console.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "hw/qdev-core.h"
@@ -121,6 +122,7 @@ struct QemuConsole {
DisplayState *ds;
DisplaySurface *surface;
int dcls;
+ DisplayChangeListener *gl;
/* Graphic console state. */
Object *device;
@@ -260,6 +262,16 @@ void graphic_hw_update(QemuConsole *con)
}
}
+void graphic_hw_gl_block(QemuConsole *con, bool block)
+{
+ if (!con) {
+ con = active_console;
+ }
+ if (con && con->hw_ops->gl_block) {
+ con->hw_ops->gl_block(con->hw, block);
+ }
+}
+
void graphic_hw_invalidate(QemuConsole *con)
{
if (!con) {
@@ -375,42 +387,29 @@ static void vga_bitblt(QemuConsole *con,
#include "vgafont.h"
-#ifndef CONFIG_CURSES
-enum color_names {
- COLOR_BLACK = 0,
- COLOR_RED = 1,
- COLOR_GREEN = 2,
- COLOR_YELLOW = 3,
- COLOR_BLUE = 4,
- COLOR_MAGENTA = 5,
- COLOR_CYAN = 6,
- COLOR_WHITE = 7
-};
-#endif
-
#define QEMU_RGB(r, g, b) \
{ .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
static const pixman_color_t color_table_rgb[2][8] = {
{ /* dark */
- QEMU_RGB(0x00, 0x00, 0x00), /* black */
- QEMU_RGB(0xaa, 0x00, 0x00), /* red */
- QEMU_RGB(0x00, 0xaa, 0x00), /* green */
- QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
- QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
- QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
- QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
- QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
+ [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xaa), /* blue */
+ [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xaa, 0x00), /* green */
+ [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xaa, 0xaa), /* cyan */
+ [QEMU_COLOR_RED] = QEMU_RGB(0xaa, 0x00, 0x00), /* red */
+ [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xaa, 0x00, 0xaa), /* magenta */
+ [QEMU_COLOR_YELLOW] = QEMU_RGB(0xaa, 0xaa, 0x00), /* yellow */
+ [QEMU_COLOR_WHITE] = QEMU_RGB(0xaa, 0xaa, 0xaa), /* white */
},
{ /* bright */
- QEMU_RGB(0x00, 0x00, 0x00), /* black */
- QEMU_RGB(0xff, 0x00, 0x00), /* red */
- QEMU_RGB(0x00, 0xff, 0x00), /* green */
- QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
- QEMU_RGB(0x00, 0x00, 0xff), /* blue */
- QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
- QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
- QEMU_RGB(0xff, 0xff, 0xff), /* white */
+ [QEMU_COLOR_BLACK] = QEMU_RGB(0x00, 0x00, 0x00), /* black */
+ [QEMU_COLOR_BLUE] = QEMU_RGB(0x00, 0x00, 0xff), /* blue */
+ [QEMU_COLOR_GREEN] = QEMU_RGB(0x00, 0xff, 0x00), /* green */
+ [QEMU_COLOR_CYAN] = QEMU_RGB(0x00, 0xff, 0xff), /* cyan */
+ [QEMU_COLOR_RED] = QEMU_RGB(0xff, 0x00, 0x00), /* red */
+ [QEMU_COLOR_MAGENTA] = QEMU_RGB(0xff, 0x00, 0xff), /* magenta */
+ [QEMU_COLOR_YELLOW] = QEMU_RGB(0xff, 0xff, 0x00), /* yellow */
+ [QEMU_COLOR_WHITE] = QEMU_RGB(0xff, 0xff, 0xff), /* white */
}
};
@@ -449,7 +448,7 @@ static void text_console_resize(QemuConsole *s)
if (s->width < w1)
w1 = s->width;
- cells = g_malloc(s->width * s->total_height * sizeof(TextCell));
+ cells = g_new(TextCell, s->width * s->total_height);
for(y = 0; y < s->total_height; y++) {
c = &cells[y * s->width];
if (w1 > 0) {
@@ -559,7 +558,7 @@ static void console_refresh(QemuConsole *s)
}
vga_fill_rect(s, 0, 0, surface_width(surface), surface_height(surface),
- color_table_rgb[0][COLOR_BLACK]);
+ color_table_rgb[0][QEMU_COLOR_BLACK]);
y1 = s->y_displayed;
for (y = 0; y < s->height; y++) {
c = s->cells + y1 * s->width;
@@ -697,53 +696,53 @@ static void console_handle_escape(QemuConsole *s)
break;
/* set foreground color */
case 30:
- s->t_attrib.fgcol=COLOR_BLACK;
+ s->t_attrib.fgcol = QEMU_COLOR_BLACK;
break;
case 31:
- s->t_attrib.fgcol=COLOR_RED;
+ s->t_attrib.fgcol = QEMU_COLOR_RED;
break;
case 32:
- s->t_attrib.fgcol=COLOR_GREEN;
+ s->t_attrib.fgcol = QEMU_COLOR_GREEN;
break;
case 33:
- s->t_attrib.fgcol=COLOR_YELLOW;
+ s->t_attrib.fgcol = QEMU_COLOR_YELLOW;
break;
case 34:
- s->t_attrib.fgcol=COLOR_BLUE;
+ s->t_attrib.fgcol = QEMU_COLOR_BLUE;
break;
case 35:
- s->t_attrib.fgcol=COLOR_MAGENTA;
+ s->t_attrib.fgcol = QEMU_COLOR_MAGENTA;
break;
case 36:
- s->t_attrib.fgcol=COLOR_CYAN;
+ s->t_attrib.fgcol = QEMU_COLOR_CYAN;
break;
case 37:
- s->t_attrib.fgcol=COLOR_WHITE;
+ s->t_attrib.fgcol = QEMU_COLOR_WHITE;
break;
/* set background color */
case 40:
- s->t_attrib.bgcol=COLOR_BLACK;
+ s->t_attrib.bgcol = QEMU_COLOR_BLACK;
break;
case 41:
- s->t_attrib.bgcol=COLOR_RED;
+ s->t_attrib.bgcol = QEMU_COLOR_RED;
break;
case 42:
- s->t_attrib.bgcol=COLOR_GREEN;
+ s->t_attrib.bgcol = QEMU_COLOR_GREEN;
break;
case 43:
- s->t_attrib.bgcol=COLOR_YELLOW;
+ s->t_attrib.bgcol = QEMU_COLOR_YELLOW;
break;
case 44:
- s->t_attrib.bgcol=COLOR_BLUE;
+ s->t_attrib.bgcol = QEMU_COLOR_BLUE;
break;
case 45:
- s->t_attrib.bgcol=COLOR_MAGENTA;
+ s->t_attrib.bgcol = QEMU_COLOR_MAGENTA;
break;
case 46:
- s->t_attrib.bgcol=COLOR_CYAN;
+ s->t_attrib.bgcol = QEMU_COLOR_CYAN;
break;
case 47:
- s->t_attrib.bgcol=COLOR_WHITE;
+ s->t_attrib.bgcol = QEMU_COLOR_WHITE;
break;
}
}
@@ -758,6 +757,31 @@ static void console_clear_xy(QemuConsole *s, int x, int y)
update_xy(s, x, y);
}
+static void console_put_one(QemuConsole *s, int ch)
+{
+ TextCell *c;
+ int y1;
+ if (s->x >= s->width) {
+ /* line wrap */
+ s->x = 0;
+ console_put_lf(s);
+ }
+ y1 = (s->y_base + s->y) % s->total_height;
+ c = &s->cells[y1 * s->width + s->x];
+ c->ch = ch;
+ c->t_attrib = s->t_attrib;
+ update_xy(s, s->x, s->y);
+ s->x++;
+}
+
+static void console_respond_str(QemuConsole *s, const char *buf)
+{
+ while (*buf) {
+ console_put_one(s, *buf);
+ buf++;
+ }
+}
+
/* set cursor, checking bounds */
static void set_cursor(QemuConsole *s, int x, int y)
{
@@ -780,9 +804,9 @@ static void set_cursor(QemuConsole *s, int x, int y)
static void console_putchar(QemuConsole *s, int ch)
{
- TextCell *c;
- int y1, i;
+ int i;
int x, y;
+ char response[40];
switch(s->state) {
case TTY_STATE_NORM:
@@ -818,17 +842,7 @@ static void console_putchar(QemuConsole *s, int ch)
s->state = TTY_STATE_ESC;
break;
default:
- if (s->x >= s->width) {
- /* line wrap */
- s->x = 0;
- console_put_lf(s);
- }
- y1 = (s->y_base + s->y) % s->total_height;
- c = &s->cells[y1 * s->width + s->x];
- c->ch = ch;
- c->t_attrib = s->t_attrib;
- update_xy(s, s->x, s->y);
- s->x++;
+ console_put_one(s, ch);
break;
}
break;
@@ -957,8 +971,19 @@ static void console_putchar(QemuConsole *s, int ch)
console_handle_escape(s);
break;
case 'n':
- /* report cursor position */
- /* TODO: send ESC[row;colR */
+ switch (s->esc_params[0]) {
+ case 5:
+ /* report console status (always succeed)*/
+ console_respond_str(s, "\033[0n");
+ break;
+ case 6:
+ /* report cursor position */
+ sprintf(response, "\033[%d;%dR",
+ (s->y_base + s->y) % s->total_height + 1,
+ s->x + 1);
+ console_respond_str(s, response);
+ break;
+ }
break;
case 's':
/* save cursor position */
@@ -1107,7 +1132,7 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
}
}
-static const int qcode_to_keysym[Q_KEY_CODE_MAX] = {
+static const int qcode_to_keysym[Q_KEY_CODE__MAX] = {
[Q_KEY_CODE_UP] = QEMU_KEY_UP,
[Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
[Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
@@ -1164,11 +1189,13 @@ static void text_console_update(void *opaque, console_ch_t *chardata)
src = (s->y_base + s->text_y[0]) * s->width;
chardata += s->text_y[0] * s->width;
for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
- for (j = 0; j < s->width; j ++, src ++)
- console_write_ch(chardata ++, s->cells[src].ch |
- (s->cells[src].t_attrib.fgcol << 12) |
- (s->cells[src].t_attrib.bgcol << 8) |
- (s->cells[src].t_attrib.bold << 21));
+ for (j = 0; j < s->width; j++, src++) {
+ console_write_ch(chardata ++,
+ ATTR2CHTYPE(s->cells[src].ch,
+ s->cells[src].t_attrib.fgcol,
+ s->cells[src].t_attrib.bgcol,
+ s->cells[src].t_attrib.bold));
+ }
dpy_text_update(s, s->text_x[0], s->text_y[0],
s->text_x[1] - s->text_x[0], i - s->text_y[0]);
s->text_x[0] = s->width;
@@ -1265,6 +1292,17 @@ DisplaySurface *qemu_create_displaysurface_from(int width, int height,
return surface;
}
+DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image)
+{
+ DisplaySurface *surface = g_new0(DisplaySurface, 1);
+
+ trace_displaysurface_create_pixman(surface);
+ surface->format = pixman_image_get_format(image);
+ surface->image = pixman_image_ref(image);
+
+ return surface;
+}
+
static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image,
void *unused)
{
@@ -1305,8 +1343,8 @@ static DisplaySurface *qemu_create_message_surface(int w, int h,
const char *msg)
{
DisplaySurface *surface = qemu_create_displaysurface(w, h);
- pixman_color_t bg = color_table_rgb[0][COLOR_BLACK];
- pixman_color_t fg = color_table_rgb[0][COLOR_WHITE];
+ pixman_color_t bg = color_table_rgb[0][QEMU_COLOR_BLACK];
+ pixman_color_t fg = color_table_rgb[0][QEMU_COLOR_WHITE];
pixman_image_t *glyph;
int len, x, y, i;
@@ -1332,6 +1370,11 @@ void qemu_free_displaysurface(DisplaySurface *surface)
g_free(surface);
}
+bool console_has_gl(QemuConsole *con)
+{
+ return con->gl != NULL;
+}
+
void register_displaychangelistener(DisplayChangeListener *dcl)
{
static const char nodev[] =
@@ -1339,6 +1382,17 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
static DisplaySurface *dummy;
QemuConsole *con;
+ if (dcl->ops->dpy_gl_ctx_create) {
+ /* display has opengl support */
+ assert(dcl->con);
+ if (dcl->con->gl) {
+ fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
+ dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
+ exit(1);
+ }
+ dcl->con->gl = dcl;
+ }
+
trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
dcl->ds = get_alloc_displaystate();
QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
@@ -1417,9 +1471,13 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
- int width = surface_width(con->surface);
- int height = surface_height(con->surface);
+ int width = w;
+ int height = h;
+ if (con->surface) {
+ width = surface_width(con->surface);
+ height = surface_height(con->surface);
+ }
x = MAX(x, 0);
y = MAX(y, 0);
x = MIN(x, width);
@@ -1619,6 +1677,48 @@ bool dpy_cursor_define_supported(QemuConsole *con)
return false;
}
+QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
+ struct QEMUGLParams *qparams)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
+}
+
+void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
+}
+
+int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
+}
+
+QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
+}
+
+void dpy_gl_scanout(QemuConsole *con,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_scanout(con->gl, backing_id,
+ backing_y_0_top,
+ x, y, width, height);
+}
+
+void dpy_gl_update(QemuConsole *con,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
+}
+
/***********************************************************/
/* register display */
@@ -1727,6 +1827,29 @@ QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head)
return NULL;
}
+QemuConsole *qemu_console_lookup_by_device_name(const char *device_id,
+ uint32_t head, Error **errp)
+{
+ DeviceState *dev;
+ QemuConsole *con;
+
+ dev = qdev_find_recursive(sysbus_get_default(), device_id);
+ if (dev == NULL) {
+ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+ "Device '%s' not found", device_id);
+ return NULL;
+ }
+
+ con = qemu_console_lookup_by_device(dev, head);
+ if (con == NULL) {
+ error_setg(errp, "Device %s (head %d) is not bound to a QemuConsole",
+ device_id, head);
+ return NULL;
+ }
+
+ return con;
+}
+
bool qemu_console_is_visible(QemuConsole *con)
{
return (con == active_console) || (con->dcls > 0);
@@ -1878,8 +2001,8 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
s->t_attrib_default.blink = 0;
s->t_attrib_default.invers = 0;
s->t_attrib_default.unvisible = 0;
- s->t_attrib_default.fgcol = COLOR_WHITE;
- s->t_attrib_default.bgcol = COLOR_BLACK;
+ s->t_attrib_default.fgcol = QEMU_COLOR_WHITE;
+ s->t_attrib_default.bgcol = QEMU_COLOR_BLACK;
/* set current text attributes to default */
s->t_attrib = s->t_attrib_default;
text_console_resize(s);
@@ -1888,7 +2011,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
char msg[128];
int len;
- s->t_attrib.bgcol = COLOR_BLUE;
+ s->t_attrib.bgcol = QEMU_COLOR_BLUE;
len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label);
console_puts(chr, (uint8_t*)msg, len);
s->t_attrib = s->t_attrib_default;
@@ -1899,14 +2022,18 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
chr->init(chr);
}
-static CharDriverState *text_console_init(ChardevVC *vc)
+static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
{
+ ChardevCommon *common = qapi_ChardevVC_base(vc);
CharDriverState *chr;
QemuConsole *s;
unsigned width = 0;
unsigned height = 0;
- chr = qemu_chr_alloc();
+ chr = qemu_chr_alloc(common, errp);
+ if (!chr) {
+ return NULL;
+ }
if (vc->has_width) {
width = vc->width;
@@ -1930,6 +2057,7 @@ static CharDriverState *text_console_init(ChardevVC *vc)
if (!s) {
g_free(chr);
+ error_setg(errp, "cannot create text console");
return NULL;
}
@@ -1949,9 +2077,10 @@ static CharDriverState *text_console_init(ChardevVC *vc)
static VcHandler *vc_handler = text_console_init;
-CharDriverState *vc_init(ChardevVC *vc)
+static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
+ ChardevReturn *ret, Error **errp)
{
- return vc_handler(vc);
+ return vc_handler(backend->u.vc.data, errp);
}
void register_vc_handler(VcHandler *handler)
@@ -1991,31 +2120,33 @@ static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend,
Error **errp)
{
int val;
+ ChardevVC *vc;
- backend->vc = g_new0(ChardevVC, 1);
+ vc = backend->u.vc.data = g_new0(ChardevVC, 1);
+ qemu_chr_parse_common(opts, qapi_ChardevVC_base(vc));
val = qemu_opt_get_number(opts, "width", 0);
if (val != 0) {
- backend->vc->has_width = true;
- backend->vc->width = val;
+ vc->has_width = true;
+ vc->width = val;
}
val = qemu_opt_get_number(opts, "height", 0);
if (val != 0) {
- backend->vc->has_height = true;
- backend->vc->height = val;
+ vc->has_height = true;
+ vc->height = val;
}
val = qemu_opt_get_number(opts, "cols", 0);
if (val != 0) {
- backend->vc->has_cols = true;
- backend->vc->cols = val;
+ vc->has_cols = true;
+ vc->cols = val;
}
val = qemu_opt_get_number(opts, "rows", 0);
if (val != 0) {
- backend->vc->has_rows = true;
- backend->vc->rows = val;
+ vc->has_rows = true;
+ vc->rows = val;
}
}
@@ -2030,7 +2161,8 @@ static const TypeInfo qemu_console_info = {
static void register_types(void)
{
type_register_static(&qemu_console_info);
- register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc);
+ register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc,
+ vc_init);
}
type_init(register_types);
diff --git a/qemu/ui/curses.c b/qemu/ui/curses.c
index 8edb038bb..b47558956 100644
--- a/qemu/ui/curses.c
+++ b/qemu/ui/curses.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include <curses.h>
#ifndef _WIN32
@@ -42,6 +43,8 @@ static WINDOW *screenpad = NULL;
static int width, height, gwidth, gheight, invalidate;
static int px, py, sminx, sminy, smaxx, smaxy;
+chtype vga_to_curses[256];
+
static void curses_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
@@ -331,8 +334,14 @@ static void curses_atexit(void)
static void curses_setup(void)
{
int i, colour_default[8] = {
- COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
- COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE,
+ [QEMU_COLOR_BLACK] = COLOR_BLACK,
+ [QEMU_COLOR_BLUE] = COLOR_BLUE,
+ [QEMU_COLOR_GREEN] = COLOR_GREEN,
+ [QEMU_COLOR_CYAN] = COLOR_CYAN,
+ [QEMU_COLOR_RED] = COLOR_RED,
+ [QEMU_COLOR_MAGENTA] = COLOR_MAGENTA,
+ [QEMU_COLOR_YELLOW] = COLOR_YELLOW,
+ [QEMU_COLOR_WHITE] = COLOR_WHITE,
};
/* input as raw as possible, let everything be interpreted
@@ -341,8 +350,56 @@ static void curses_setup(void)
nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
start_color(); raw(); scrollok(stdscr, FALSE);
- for (i = 0; i < 64; i ++)
+ /* Make color pair to match color format (3bits bg:3bits fg) */
+ for (i = 0; i < 64; i++) {
init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
+ }
+ /* Set default color for more than 64 for safety. */
+ for (i = 64; i < COLOR_PAIRS; i++) {
+ init_pair(i, COLOR_WHITE, COLOR_BLACK);
+ }
+
+ /*
+ * Setup mapping for vga to curses line graphics.
+ * FIXME: for better font, have to use ncursesw and setlocale()
+ */
+#if 0
+ /* FIXME: map from where? */
+ ACS_S1;
+ ACS_S3;
+ ACS_S7;
+ ACS_S9;
+#endif
+ /* ACS_* is not constant. So, we can't initialize statically. */
+ vga_to_curses['\0'] = ' ';
+ vga_to_curses[0x04] = ACS_DIAMOND;
+ vga_to_curses[0x0a] = ACS_RARROW;
+ vga_to_curses[0x0b] = ACS_LARROW;
+ vga_to_curses[0x18] = ACS_UARROW;
+ vga_to_curses[0x19] = ACS_DARROW;
+ vga_to_curses[0x9c] = ACS_STERLING;
+ vga_to_curses[0xb0] = ACS_BOARD;
+ vga_to_curses[0xb1] = ACS_CKBOARD;
+ vga_to_curses[0xb3] = ACS_VLINE;
+ vga_to_curses[0xb4] = ACS_RTEE;
+ vga_to_curses[0xbf] = ACS_URCORNER;
+ vga_to_curses[0xc0] = ACS_LLCORNER;
+ vga_to_curses[0xc1] = ACS_BTEE;
+ vga_to_curses[0xc2] = ACS_TTEE;
+ vga_to_curses[0xc3] = ACS_LTEE;
+ vga_to_curses[0xc4] = ACS_HLINE;
+ vga_to_curses[0xc5] = ACS_PLUS;
+ vga_to_curses[0xce] = ACS_LANTERN;
+ vga_to_curses[0xd8] = ACS_NEQUAL;
+ vga_to_curses[0xd9] = ACS_LRCORNER;
+ vga_to_curses[0xda] = ACS_ULCORNER;
+ vga_to_curses[0xdb] = ACS_BLOCK;
+ vga_to_curses[0xe3] = ACS_PI;
+ vga_to_curses[0xf1] = ACS_PLMINUS;
+ vga_to_curses[0xf2] = ACS_GEQUAL;
+ vga_to_curses[0xf3] = ACS_LEQUAL;
+ vga_to_curses[0xf8] = ACS_DEGREE;
+ vga_to_curses[0xfe] = ACS_BULLET;
}
static void curses_keyboard_setup(void)
@@ -382,7 +439,7 @@ void curses_display_init(DisplayState *ds, int full_screen)
curses_winch_init();
- dcl = (DisplayChangeListener *) g_malloc0(sizeof(DisplayChangeListener));
+ dcl = g_new0(DisplayChangeListener, 1);
dcl->ops = &dcl_ops;
register_displaychangelistener(dcl);
diff --git a/qemu/ui/curses_keys.h b/qemu/ui/curses_keys.h
index 18ce6dcee..f7467449b 100644
--- a/qemu/ui/curses_keys.h
+++ b/qemu/ui/curses_keys.h
@@ -29,8 +29,7 @@
#include "keymaps.h"
-#define KEY_RELEASE 0x80
-#define KEY_MASK 0x7f
+#define KEY_MASK SCANCODE_KEYMASK
#define GREY_CODE 0xe0
#define GREY SCANCODE_GREY
#define SHIFT_CODE 0x2a
@@ -60,6 +59,8 @@ static const int curses2keysym[CURSES_KEYS] = {
['\n'] = KEY_ENTER,
[27] = 27,
[KEY_BTAB] = '\t' | KEYSYM_SHIFT,
+ [KEY_SPREVIOUS] = KEY_PPAGE | KEYSYM_SHIFT,
+ [KEY_SNEXT] = KEY_NPAGE | KEYSYM_SHIFT,
};
static const int curses2keycode[CURSES_KEYS] = {
@@ -149,6 +150,9 @@ static const int curses2keycode[CURSES_KEYS] = {
[KEY_IC] = 82 | GREY, /* Insert */
[KEY_DC] = 83 | GREY, /* Delete */
+ [KEY_SPREVIOUS] = 73 | GREY | SHIFT, /* Shift + Page Up */
+ [KEY_SNEXT] = 81 | GREY | SHIFT, /* Shift + Page Down */
+
['!'] = 2 | SHIFT,
['@'] = 3 | SHIFT,
['#'] = 4 | SHIFT,
diff --git a/qemu/ui/cursor.c b/qemu/ui/cursor.c
index 2b8dd3fa5..a276e01f1 100644
--- a/qemu/ui/cursor.c
+++ b/qemu/ui/cursor.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
diff --git a/qemu/ui/egl-context.c b/qemu/ui/egl-context.c
new file mode 100644
index 000000000..3a02b68d1
--- /dev/null
+++ b/qemu/ui/egl-context.c
@@ -0,0 +1,35 @@
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "ui/egl-context.h"
+
+QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ EGLContext ctx;
+ EGLint ctx_att[] = {
+ EGL_CONTEXT_CLIENT_VERSION, params->major_ver,
+ EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver,
+ EGL_NONE
+ };
+
+ ctx = eglCreateContext(qemu_egl_display, qemu_egl_config,
+ eglGetCurrentContext(), ctx_att);
+ return ctx;
+}
+
+void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ eglDestroyContext(qemu_egl_display, ctx);
+}
+
+int qemu_egl_make_context_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ return eglMakeCurrent(qemu_egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
+}
+
+QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl)
+{
+ return eglGetCurrentContext();
+}
diff --git a/qemu/ui/egl-helpers.c b/qemu/ui/egl-helpers.c
index 87d77afaa..558edfdeb 100644
--- a/qemu/ui/egl-helpers.c
+++ b/qemu/ui/egl-helpers.c
@@ -1,12 +1,6 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
+#include "qemu/osdep.h"
#include <glob.h>
+#include <dirent.h>
#include "ui/egl-helpers.h"
@@ -27,6 +21,133 @@ static int egl_debug;
/* ---------------------------------------------------------------------- */
+#ifdef CONFIG_OPENGL_DMABUF
+
+int qemu_egl_rn_fd;
+struct gbm_device *qemu_egl_rn_gbm_dev;
+EGLContext qemu_egl_rn_ctx;
+
+int qemu_egl_rendernode_open(void)
+{
+ DIR *dir;
+ struct dirent *e;
+ int r, fd;
+ char *p;
+
+ dir = opendir("/dev/dri");
+ if (!dir) {
+ return -1;
+ }
+
+ fd = -1;
+ while ((e = readdir(dir))) {
+ if (e->d_type != DT_CHR) {
+ continue;
+ }
+
+ if (strncmp(e->d_name, "renderD", 7)) {
+ continue;
+ }
+
+ r = asprintf(&p, "/dev/dri/%s", e->d_name);
+ if (r < 0) {
+ return -1;
+ }
+
+ r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
+ if (r < 0) {
+ free(p);
+ continue;
+ }
+ fd = r;
+ free(p);
+ break;
+ }
+
+ closedir(dir);
+ if (fd < 0) {
+ return -1;
+ }
+ return fd;
+}
+
+int egl_rendernode_init(void)
+{
+ qemu_egl_rn_fd = -1;
+
+ qemu_egl_rn_fd = qemu_egl_rendernode_open();
+ if (qemu_egl_rn_fd == -1) {
+ fprintf(stderr, "egl: no drm render node available\n");
+ goto err;
+ }
+
+ qemu_egl_rn_gbm_dev = gbm_create_device(qemu_egl_rn_fd);
+ if (!qemu_egl_rn_gbm_dev) {
+ fprintf(stderr, "egl: gbm_create_device failed\n");
+ goto err;
+ }
+
+ qemu_egl_init_dpy((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, false, false);
+
+ if (!epoxy_has_egl_extension(qemu_egl_display,
+ "EGL_KHR_surfaceless_context")) {
+ fprintf(stderr, "egl: EGL_KHR_surfaceless_context not supported\n");
+ goto err;
+ }
+ if (!epoxy_has_egl_extension(qemu_egl_display,
+ "EGL_MESA_image_dma_buf_export")) {
+ fprintf(stderr, "egl: EGL_MESA_image_dma_buf_export not supported\n");
+ goto err;
+ }
+
+ qemu_egl_rn_ctx = qemu_egl_init_ctx();
+ if (!qemu_egl_rn_ctx) {
+ fprintf(stderr, "egl: egl_init_ctx failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ if (qemu_egl_rn_gbm_dev) {
+ gbm_device_destroy(qemu_egl_rn_gbm_dev);
+ }
+ if (qemu_egl_rn_fd != -1) {
+ close(qemu_egl_rn_fd);
+ }
+
+ return -1;
+}
+
+int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc)
+{
+ EGLImageKHR image;
+ EGLint num_planes, fd;
+
+ image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(),
+ EGL_GL_TEXTURE_2D_KHR,
+ (EGLClientBuffer)(unsigned long)tex_id,
+ NULL);
+ if (!image) {
+ return -1;
+ }
+
+ eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc,
+ &num_planes, NULL);
+ if (num_planes != 1) {
+ eglDestroyImageKHR(qemu_egl_display, image);
+ return -1;
+ }
+ eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL);
+ eglDestroyImageKHR(qemu_egl_display, image);
+
+ return fd;
+}
+
+#endif /* CONFIG_OPENGL_DMABUF */
+
+/* ---------------------------------------------------------------------- */
+
EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win)
{
EGLSurface esurface;
diff --git a/qemu/ui/gtk-egl.c b/qemu/ui/gtk-egl.c
index 15b41f2ba..431457c74 100644
--- a/qemu/ui/gtk-egl.c
+++ b/qemu/ui/gtk-egl.c
@@ -11,6 +11,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "trace.h"
@@ -21,6 +22,29 @@
#include "sysemu/sysemu.h"
+static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
+{
+ if (vc->gfx.scanout_mode == scanout) {
+ return;
+ }
+
+ vc->gfx.scanout_mode = scanout;
+ if (!vc->gfx.scanout_mode) {
+ if (vc->gfx.fbo_id) {
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffers(1, &vc->gfx.fbo_id);
+ vc->gfx.fbo_id = 0;
+ }
+ if (vc->gfx.surface) {
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+}
+
/** DisplayState Callbacks (opengl version) **/
void gd_egl_init(VirtualConsole *vc)
@@ -50,19 +74,26 @@ void gd_egl_draw(VirtualConsole *vc)
GdkWindow *window;
int ww, wh;
- if (!vc->gfx.gls || !vc->gfx.ds) {
+ if (!vc->gfx.gls) {
return;
}
- eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
- vc->gfx.esurface, vc->gfx.ectx);
+ if (vc->gfx.scanout_mode) {
+ gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
+ } else {
+ if (!vc->gfx.ds) {
+ return;
+ }
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
- window = gtk_widget_get_window(vc->gfx.drawing_area);
- gdk_drawable_get_size(window, &ww, &wh);
- surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
- surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ gdk_drawable_get_size(window, &ww, &wh);
+ surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+ surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
- eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+ eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+ }
}
void gd_egl_update(DisplayChangeListener *dcl,
@@ -99,6 +130,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl)
if (vc->gfx.glupdates) {
vc->gfx.glupdates = 0;
+ gtk_egl_set_scanout_mode(vc, false);
gd_egl_draw(vc);
}
}
@@ -128,6 +160,81 @@ void gd_egl_switch(DisplayChangeListener *dcl,
}
}
+QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+ return qemu_egl_create_context(dcl, params);
+}
+
+void gd_egl_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.x = x;
+ vc->gfx.y = y;
+ vc->gfx.w = w;
+ vc->gfx.h = h;
+ vc->gfx.tex_id = backing_id;
+ vc->gfx.y0_top = backing_y_0_top;
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+
+ if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
+ gtk_egl_set_scanout_mode(vc, false);
+ return;
+ }
+
+ gtk_egl_set_scanout_mode(vc, true);
+ if (!vc->gfx.fbo_id) {
+ glGenFramebuffers(1, &vc->gfx.fbo_id);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, vc->gfx.tex_id, 0);
+}
+
+void gd_egl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ GdkWindow *window;
+ int ww, wh, y1, y2;
+
+ if (!vc->gfx.scanout_mode) {
+ return;
+ }
+ if (!vc->gfx.fbo_id) {
+ return;
+ }
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ gdk_drawable_get_size(window, &ww, &wh);
+ glViewport(0, 0, ww, wh);
+ y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
+ y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
+ glBlitFramebuffer(0, y1, vc->gfx.w, y2,
+ 0, 0, ww, wh,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+
+ eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+}
+
void gtk_egl_init(void)
{
GdkDisplay *gdk_display = gdk_display_get_default();
@@ -139,3 +246,12 @@ void gtk_egl_init(void)
display_opengl = 1;
}
+
+int gd_egl_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, ctx);
+}
diff --git a/qemu/ui/gtk-gl-area.c b/qemu/ui/gtk-gl-area.c
new file mode 100644
index 000000000..b86ff3cbe
--- /dev/null
+++ b/qemu/ui/gtk-gl-area.c
@@ -0,0 +1,224 @@
+/*
+ * GTK UI -- glarea opengl code.
+ *
+ * Requires 3.16+ (GtkGLArea widget).
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+
+#include "trace.h"
+
+#include "ui/console.h"
+#include "ui/gtk.h"
+#include "ui/egl-helpers.h"
+
+#include "sysemu/sysemu.h"
+
+static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
+{
+ if (vc->gfx.scanout_mode == scanout) {
+ return;
+ }
+
+ vc->gfx.scanout_mode = scanout;
+ if (!vc->gfx.scanout_mode) {
+ if (vc->gfx.fbo_id) {
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffers(1, &vc->gfx.fbo_id);
+ vc->gfx.fbo_id = 0;
+ }
+ if (vc->gfx.surface) {
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+}
+
+/** DisplayState Callbacks (opengl version) **/
+
+void gd_gl_area_draw(VirtualConsole *vc)
+{
+ int ww, wh, y1, y2;
+
+ if (!vc->gfx.gls) {
+ return;
+ }
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+ wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
+
+ if (vc->gfx.scanout_mode) {
+ if (!vc->gfx.fbo_id) {
+ return;
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
+ /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
+
+ glViewport(0, 0, ww, wh);
+ y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
+ y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
+ glBlitFramebuffer(0, y1, vc->gfx.w, y2,
+ 0, 0, ww, wh,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ } else {
+ if (!vc->gfx.ds) {
+ return;
+ }
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+
+ surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+ surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+}
+
+void gd_gl_area_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ if (!vc->gfx.gls || !vc->gfx.ds) {
+ return;
+ }
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
+ vc->gfx.glupdates++;
+}
+
+void gd_gl_area_refresh(DisplayChangeListener *dcl)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ if (!vc->gfx.gls) {
+ if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
+ return;
+ }
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ vc->gfx.gls = console_gl_init_context();
+ if (vc->gfx.ds) {
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+
+ graphic_hw_update(dcl->con);
+
+ if (vc->gfx.glupdates) {
+ vc->gfx.glupdates = 0;
+ gtk_gl_area_set_scanout_mode(vc, false);
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+ }
+}
+
+void gd_gl_area_switch(DisplayChangeListener *dcl,
+ DisplaySurface *surface)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ bool resized = true;
+
+ trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
+
+ if (vc->gfx.ds &&
+ surface_width(vc->gfx.ds) == surface_width(surface) &&
+ surface_height(vc->gfx.ds) == surface_height(surface)) {
+ resized = false;
+ }
+
+ if (vc->gfx.gls) {
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, surface);
+ }
+ vc->gfx.ds = surface;
+
+ if (resized) {
+ gd_update_windowsize(vc);
+ }
+}
+
+QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ GdkWindow *window;
+ GdkGLContext *ctx;
+ GError *err = NULL;
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ ctx = gdk_window_create_gl_context(window, &err);
+ gdk_gl_context_set_required_version(ctx,
+ params->major_ver,
+ params->minor_ver);
+ gdk_gl_context_realize(ctx, &err);
+ return ctx;
+}
+
+void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ /* FIXME */
+}
+
+void gd_gl_area_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.x = x;
+ vc->gfx.y = y;
+ vc->gfx.w = w;
+ vc->gfx.h = h;
+ vc->gfx.tex_id = backing_id;
+ vc->gfx.y0_top = backing_y_0_top;
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+
+ if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
+ gtk_gl_area_set_scanout_mode(vc, false);
+ return;
+ }
+
+ gtk_gl_area_set_scanout_mode(vc, true);
+ if (!vc->gfx.fbo_id) {
+ glGenFramebuffers(1, &vc->gfx.fbo_id);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, vc->gfx.tex_id, 0);
+}
+
+void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+}
+
+void gtk_gl_area_init(void)
+{
+ display_opengl = 1;
+}
+
+QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl)
+{
+ return gdk_gl_context_get_current();
+}
+
+int gd_gl_area_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ gdk_gl_context_make_current(ctx);
+ return 0;
+}
diff --git a/qemu/ui/gtk.c b/qemu/ui/gtk.c
index df2a79e7a..f372a6d5a 100644
--- a/qemu/ui/gtk.c
+++ b/qemu/ui/gtk.c
@@ -34,7 +34,9 @@
#define GETTEXT_PACKAGE "qemu"
#define LOCALEDIR "po"
+#include "qemu/osdep.h"
#include "qemu-common.h"
+#include "qemu/cutils.h"
#include "ui/console.h"
#include "ui/gtk.h"
@@ -104,6 +106,15 @@
#define GDK_KEY_Pause GDK_Pause
#endif
+/* Some older mingw versions lack this constant or have
+ * it conditionally defined */
+#ifdef _WIN32
+# ifndef MAPVK_VK_TO_VSC
+# define MAPVK_VK_TO_VSC 0
+# endif
+#endif
+
+
#define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
static const int modifier_keycode[] = {
@@ -165,8 +176,10 @@ struct GtkDisplayState {
bool ignore_keys;
};
-static void gd_grab_pointer(VirtualConsole *vc);
+static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
static void gd_ungrab_pointer(GtkDisplayState *s);
+static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
+static void gd_ungrab_keyboard(GtkDisplayState *s);
/** Utility Functions **/
@@ -356,6 +369,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
GtkWidget *area = vc->gfx.drawing_area;
int ww, wh;
gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
+#if defined(CONFIG_GTK_GL)
+ if (vc->gfx.gls) {
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+ return;
+ }
+#endif
gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
}
@@ -596,6 +615,27 @@ static const DisplayChangeListenerOps dcl_ops = {
/** DisplayState Callbacks (opengl version) **/
+#if defined(CONFIG_GTK_GL)
+
+static const DisplayChangeListenerOps dcl_gl_area_ops = {
+ .dpy_name = "gtk-egl",
+ .dpy_gfx_update = gd_gl_area_update,
+ .dpy_gfx_switch = gd_gl_area_switch,
+ .dpy_gfx_check_format = console_gl_check_format,
+ .dpy_refresh = gd_gl_area_refresh,
+ .dpy_mouse_set = gd_mouse_set,
+ .dpy_cursor_define = gd_cursor_define,
+
+ .dpy_gl_ctx_create = gd_gl_area_create_context,
+ .dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
+ .dpy_gl_ctx_make_current = gd_gl_area_make_current,
+ .dpy_gl_ctx_get_current = gd_gl_area_get_current_context,
+ .dpy_gl_scanout = gd_gl_area_scanout,
+ .dpy_gl_update = gd_gl_area_scanout_flush,
+};
+
+#else
+
static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
.dpy_gfx_update = gd_egl_update,
@@ -604,9 +644,17 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_refresh = gd_egl_refresh,
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
+
+ .dpy_gl_ctx_create = gd_egl_create_context,
+ .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
+ .dpy_gl_ctx_make_current = gd_egl_make_current,
+ .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
+ .dpy_gl_scanout = gd_egl_scanout,
+ .dpy_gl_update = gd_egl_scanout_flush,
};
-#endif
+#endif /* CONFIG_GTK_GL */
+#endif /* CONFIG_OPENGL */
/** QEMU Events **/
@@ -656,6 +704,39 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
return TRUE;
}
+static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
+{
+ QemuUIInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.width = width;
+ info.height = height;
+ dpy_set_ui_info(vc->gfx.dcl.con, &info);
+}
+
+#if defined(CONFIG_GTK_GL)
+
+static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
+ void *opaque)
+{
+ VirtualConsole *vc = opaque;
+
+ if (vc->gfx.gls) {
+ gd_gl_area_draw(vc);
+ }
+ return TRUE;
+}
+
+static void gd_resize_event(GtkGLArea *area,
+ gint width, gint height, gpointer *opaque)
+{
+ VirtualConsole *vc = (void *)opaque;
+
+ gd_set_ui_info(vc, width, height);
+}
+
+#endif
+
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
VirtualConsole *vc = opaque;
@@ -666,8 +747,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
#if defined(CONFIG_OPENGL)
if (vc->gfx.gls) {
+#if defined(CONFIG_GTK_GL)
+ /* invoke render callback please */
+ return FALSE;
+#else
gd_egl_draw(vc);
return TRUE;
+#endif
}
#endif
@@ -849,13 +935,11 @@ static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
/* implicitly grab the input at the first click in the relative mode */
if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
!qemu_input_is_absolute() && s->ptr_owner != vc) {
- gd_ungrab_pointer(s);
if (!vc->window) {
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
TRUE);
} else {
- gd_grab_pointer(vc);
- gd_update_caption(s);
+ gd_grab_pointer(vc, "relative-mode-click");
}
return TRUE;
}
@@ -1092,9 +1176,8 @@ static gboolean gd_win_grab(void *opaque)
if (vc->s->ptr_owner) {
gd_ungrab_pointer(vc->s);
} else {
- gd_grab_pointer(vc);
+ gd_grab_pointer(vc, "user-request-detached-tab");
}
- gd_update_caption(vc->s);
return TRUE;
}
@@ -1141,10 +1224,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
gtk_widget_hide(s->menu_bar);
if (vc->type == GD_VC_GFX) {
gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
- if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
- gtk_check_menu_item_set_active
- (GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
- }
}
gtk_window_fullscreen(GTK_WINDOW(s->window));
s->full_screen = TRUE;
@@ -1157,8 +1236,6 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
gd_update_windowsize(vc);
- gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
- FALSE);
}
}
@@ -1257,8 +1334,16 @@ static void gd_grab_devices(VirtualConsole *vc, bool grab,
}
#endif
-static void gd_grab_keyboard(VirtualConsole *vc)
+static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
{
+ if (vc->s->kbd_owner) {
+ if (vc->s->kbd_owner == vc) {
+ return;
+ } else {
+ gd_ungrab_keyboard(vc->s);
+ }
+ }
+
#if GTK_CHECK_VERSION(3, 0, 0)
gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
@@ -1269,7 +1354,8 @@ static void gd_grab_keyboard(VirtualConsole *vc)
GDK_CURRENT_TIME);
#endif
vc->s->kbd_owner = vc;
- trace_gd_grab(vc->label, "kbd", true);
+ gd_update_caption(vc->s);
+ trace_gd_grab(vc->label, "kbd", reason);
}
static void gd_ungrab_keyboard(GtkDisplayState *s)
@@ -1286,12 +1372,22 @@ static void gd_ungrab_keyboard(GtkDisplayState *s)
#else
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
#endif
- trace_gd_grab(vc->label, "kbd", false);
+ gd_update_caption(s);
+ trace_gd_ungrab(vc->label, "kbd");
}
-static void gd_grab_pointer(VirtualConsole *vc)
+static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
{
GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
+
+ if (vc->s->ptr_owner) {
+ if (vc->s->ptr_owner == vc) {
+ return;
+ } else {
+ gd_ungrab_pointer(vc->s);
+ }
+ }
+
#if GTK_CHECK_VERSION(3, 0, 0)
GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
@@ -1318,7 +1414,8 @@ static void gd_grab_pointer(VirtualConsole *vc)
&vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
#endif
vc->s->ptr_owner = vc;
- trace_gd_grab(vc->label, "ptr", true);
+ gd_update_caption(vc->s);
+ trace_gd_grab(vc->label, "ptr", reason);
}
static void gd_ungrab_pointer(GtkDisplayState *s)
@@ -1343,7 +1440,8 @@ static void gd_ungrab_pointer(GtkDisplayState *s)
gtk_widget_get_screen(vc->gfx.drawing_area),
vc->s->grab_x_root, vc->s->grab_y_root);
#endif
- trace_gd_grab(vc->label, "ptr", false);
+ gd_update_caption(s);
+ trace_gd_ungrab(vc->label, "ptr");
}
static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
@@ -1352,16 +1450,13 @@ static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
VirtualConsole *vc = gd_vc_find_current(s);
if (gd_is_grab_active(s)) {
- if (!gd_grab_on_hover(s)) {
- gd_grab_keyboard(vc);
- }
- gd_grab_pointer(vc);
+ gd_grab_keyboard(vc, "user-request-main-window");
+ gd_grab_pointer(vc, "user-request-main-window");
} else {
gd_ungrab_keyboard(s);
gd_ungrab_pointer(s);
}
- gd_update_caption(s);
gd_update_cursor(vc);
}
@@ -1415,9 +1510,7 @@ static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
GtkDisplayState *s = vc->s;
if (gd_grab_on_hover(s)) {
- gd_ungrab_keyboard(s);
- gd_grab_keyboard(vc);
- gd_update_caption(s);
+ gd_grab_keyboard(vc, "grab-on-hover");
}
return TRUE;
}
@@ -1430,7 +1523,6 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
if (gd_grab_on_hover(s)) {
gd_ungrab_keyboard(s);
- gd_update_caption(s);
}
return TRUE;
}
@@ -1449,12 +1541,8 @@ static gboolean gd_configure(GtkWidget *widget,
GdkEventConfigure *cfg, gpointer opaque)
{
VirtualConsole *vc = opaque;
- QemuUIInfo info;
- memset(&info, 0, sizeof(info));
- info.width = cfg->width;
- info.height = cfg->height;
- dpy_set_ui_info(vc->gfx.dcl.con, &info);
+ gd_set_ui_info(vc, cfg->width, cfg->height);
return FALSE;
}
@@ -1502,15 +1590,32 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
return len;
}
+static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo)
+{
+ VirtualConsole *vc = chr->opaque;
+
+ vc->vte.echo = echo;
+}
+
static int nb_vcs;
static CharDriverState *vcs[MAX_VCS];
-static CharDriverState *gd_vc_handler(ChardevVC *unused)
+static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
{
+ ChardevCommon *common = qapi_ChardevVC_base(vc);
CharDriverState *chr;
- chr = g_malloc0(sizeof(*chr));
+ chr = qemu_chr_alloc(common, errp);
+ if (!chr) {
+ return NULL;
+ }
+
chr->chr_write = gd_vc_chr_write;
+ chr->chr_set_echo = gd_vc_chr_set_echo;
+
+ /* Temporary, until gd_vc_vte_init runs. */
+ chr->opaque = g_new0(VirtualConsole, 1);
+
/* defer OPENED events until our vc is fully initialized */
chr->explicit_be_open = true;
@@ -1524,6 +1629,24 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
{
VirtualConsole *vc = user_data;
+ if (vc->vte.echo) {
+ VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
+ int i;
+ for (i = 0; i < size; i++) {
+ uint8_t c = text[i];
+ if (c >= 128 || isprint(c)) {
+ /* 8-bit characters are considered printable. */
+ vte_terminal_feed(term, &text[i], 1);
+ } else if (c == '\r' || c == '\n') {
+ vte_terminal_feed(term, "\r\n", 2);
+ } else {
+ char ctrl[2] = { '^', 0};
+ ctrl[1] = text[i] ^ 64;
+ vte_terminal_feed(term, ctrl, 2);
+ }
+ }
+ }
+
qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
return TRUE;
}
@@ -1536,9 +1659,14 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
GtkWidget *box;
GtkWidget *scrollbar;
GtkAdjustment *vadjustment;
+ VirtualConsole *tmp_vc = chr->opaque;
vc->s = s;
+ vc->vte.echo = tmp_vc->vte.echo;
+
vc->vte.chr = chr;
+ chr->opaque = vc;
+ g_free(tmp_vc);
snprintf(buffer, sizeof(buffer), "vc%d", idx);
vc->label = g_strdup_printf("%s", vc->vte.chr->label
@@ -1548,6 +1676,15 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
vc->vte.terminal = vte_terminal_new();
g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
+ /* The documentation says that the default is UTF-8, but actually it is
+ * 7-bit ASCII at least in VTE 0.38.
+ */
+#if VTE_CHECK_VERSION(0, 40, 0)
+ vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
+#else
+ vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
+#endif
+
vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
VC_TERM_X_MIN, VC_TERM_Y_MIN);
@@ -1570,7 +1707,6 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
gtk_box_pack_start(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
- vc->vte.chr->opaque = vc;
vc->vte.box = box;
vc->vte.scrollbar = scrollbar;
@@ -1611,6 +1747,15 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
#if GTK_CHECK_VERSION(3, 0, 0)
g_signal_connect(vc->gfx.drawing_area, "draw",
G_CALLBACK(gd_draw_event), vc);
+#if defined(CONFIG_GTK_GL)
+ if (display_opengl) {
+ /* wire up GtkGlArea events */
+ g_signal_connect(vc->gfx.drawing_area, "render",
+ G_CALLBACK(gd_render_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "resize",
+ G_CALLBACK(gd_resize_event), vc);
+ }
+#endif
#else
g_signal_connect(vc->gfx.drawing_area, "expose-event",
G_CALLBACK(gd_expose_event), vc);
@@ -1719,26 +1864,13 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
- vc->gfx.drawing_area = gtk_drawing_area_new();
- gtk_widget_add_events(vc->gfx.drawing_area,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON_MOTION_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_SCROLL_MASK |
- GDK_KEY_PRESS_MASK);
- gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
-
- vc->type = GD_VC_GFX;
- vc->tab_item = vc->gfx.drawing_area;
- vc->focus = vc->gfx.drawing_area;
- gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
- vc->tab_item, gtk_label_new(vc->label));
-
#if defined(CONFIG_OPENGL)
if (display_opengl) {
+#if defined(CONFIG_GTK_GL)
+ vc->gfx.drawing_area = gtk_gl_area_new();
+ vc->gfx.dcl.ops = &dcl_gl_area_ops;
+#else
+ vc->gfx.drawing_area = gtk_drawing_area_new();
/*
* gtk_widget_set_double_buffered() was deprecated in 3.14.
* It is required for opengl rendering on X11 though. A
@@ -1754,12 +1886,32 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
#pragma GCC diagnostic pop
#endif
vc->gfx.dcl.ops = &dcl_egl_ops;
+#endif /* CONFIG_GTK_GL */
} else
#endif
{
+ vc->gfx.drawing_area = gtk_drawing_area_new();
vc->gfx.dcl.ops = &dcl_ops;
}
+
+ gtk_widget_add_events(vc->gfx.drawing_area,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK);
+ gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
+
+ vc->type = GD_VC_GFX;
+ vc->tab_item = vc->gfx.drawing_area;
+ vc->focus = vc->gfx.drawing_area;
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
+ vc->tab_item, gtk_label_new(vc->label));
+
vc->gfx.dcl.con = con;
register_displaychangelistener(&vc->gfx.dcl);
@@ -1768,6 +1920,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
+ s->free_scale = true;
}
return group;
@@ -1941,7 +2094,8 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
s->free_scale = FALSE;
- setlocale(LC_ALL, "");
+ /* LC_MESSAGES only. See early_gtk_display_init() for details */
+ setlocale(LC_MESSAGES, "");
bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
textdomain("qemu");
@@ -2010,6 +2164,24 @@ void gtk_display_init(DisplayState *ds, bool full_screen, bool grab_on_hover)
void early_gtk_display_init(int opengl)
{
+ /* The QEMU code relies on the assumption that it's always run in
+ * the C locale. Therefore it is not prepared to deal with
+ * operations that produce different results depending on the
+ * locale, such as printf's formatting of decimal numbers, and
+ * possibly others.
+ *
+ * Since GTK+ calls setlocale() by default -importing the locale
+ * settings from the environment- we must prevent it from doing so
+ * using gtk_disable_setlocale().
+ *
+ * QEMU's GTK+ UI, however, _does_ have translations for some of
+ * the menu items. As a trade-off between a functionally correct
+ * QEMU and a fully internationalized UI we support importing
+ * LC_MESSAGES from the environment (see the setlocale() call
+ * earlier in this file). This allows us to display translated
+ * messages leaving everything else untouched.
+ */
+ gtk_disable_setlocale();
gtkinit = gtk_init_check(NULL, NULL);
if (!gtkinit) {
/* don't exit yet, that'll break -help */
@@ -2022,8 +2194,12 @@ void early_gtk_display_init(int opengl)
break;
case 1: /* on */
#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_GTK_GL)
+ gtk_gl_area_init();
+#else
gtk_egl_init();
#endif
+#endif
break;
default:
g_assert_not_reached();
diff --git a/qemu/ui/input-keymap.c b/qemu/ui/input-keymap.c
index 7635cb0dc..f1e700d72 100644
--- a/qemu/ui/input-keymap.c
+++ b/qemu/ui/input-keymap.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "ui/keymaps.h"
#include "ui/input.h"
@@ -132,18 +133,18 @@ static const int qcode_to_number[] = {
[Q_KEY_CODE_RO] = 0x73,
[Q_KEY_CODE_KP_COMMA] = 0x7e,
- [Q_KEY_CODE_MAX] = 0,
+ [Q_KEY_CODE__MAX] = 0,
};
static int number_to_qcode[0x100];
int qemu_input_key_value_to_number(const KeyValue *value)
{
- if (value->kind == KEY_VALUE_KIND_QCODE) {
- return qcode_to_number[value->qcode];
+ if (value->type == KEY_VALUE_KIND_QCODE) {
+ return qcode_to_number[value->u.qcode.data];
} else {
- assert(value->kind == KEY_VALUE_KIND_NUMBER);
- return value->number;
+ assert(value->type == KEY_VALUE_KIND_NUMBER);
+ return value->u.number.data;
}
}
@@ -154,7 +155,7 @@ int qemu_input_key_number_to_qcode(uint8_t nr)
if (first) {
int qcode, number;
first = false;
- for (qcode = 0; qcode < Q_KEY_CODE_MAX; qcode++) {
+ for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) {
number = qcode_to_number[qcode];
assert(number < ARRAY_SIZE(number_to_qcode));
number_to_qcode[number] = qcode;
@@ -166,11 +167,11 @@ int qemu_input_key_number_to_qcode(uint8_t nr)
int qemu_input_key_value_to_qcode(const KeyValue *value)
{
- if (value->kind == KEY_VALUE_KIND_QCODE) {
- return value->qcode;
+ if (value->type == KEY_VALUE_KIND_QCODE) {
+ return value->u.qcode.data;
} else {
- assert(value->kind == KEY_VALUE_KIND_NUMBER);
- return qemu_input_key_number_to_qcode(value->number);
+ assert(value->type == KEY_VALUE_KIND_NUMBER);
+ return qemu_input_key_number_to_qcode(value->u.number.data);
}
}
@@ -180,8 +181,8 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
int keycode = qemu_input_key_value_to_number(value);
int count = 0;
- if (value->kind == KEY_VALUE_KIND_QCODE &&
- value->qcode == Q_KEY_CODE_PAUSE) {
+ if (value->type == KEY_VALUE_KIND_QCODE &&
+ value->u.qcode.data == Q_KEY_CODE_PAUSE) {
/* specific case */
int v = down ? 0 : 0x80;
codes[count++] = 0xe1;
diff --git a/qemu/ui/input-legacy.c b/qemu/ui/input-legacy.c
index e50f2968e..715974740 100644
--- a/qemu/ui/input-legacy.c
+++ b/qemu/ui/input-legacy.c
@@ -22,9 +22,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "ui/console.h"
-#include "qapi/error.h"
#include "qmp-commands.h"
#include "qapi-types.h"
#include "ui/keymaps.h"
@@ -38,7 +38,7 @@ struct QEMUPutMouseEntry {
/* new input core */
QemuInputHandler h;
QemuInputHandlerState *s;
- int axis[INPUT_AXIS_MAX];
+ int axis[INPUT_AXIS__MAX];
int buttons;
};
@@ -57,17 +57,18 @@ struct QEMUPutLEDEntry {
static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers =
QTAILQ_HEAD_INITIALIZER(led_handlers);
-int index_from_key(const char *key)
+int index_from_key(const char *key, size_t key_length)
{
int i;
for (i = 0; QKeyCode_lookup[i] != NULL; i++) {
- if (!strcmp(key, QKeyCode_lookup[i])) {
+ if (!strncmp(key, QKeyCode_lookup[i], key_length) &&
+ !QKeyCode_lookup[i][key_length]) {
break;
}
}
- /* Return Q_KEY_CODE_MAX if the key is invalid */
+ /* Return Q_KEY_CODE__MAX if the key is invalid */
return i;
}
@@ -109,12 +110,13 @@ static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
{
QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
int scancodes[3], i, count;
+ InputKeyEvent *key = evt->u.key.data;
if (!entry || !entry->put_kbd) {
return;
}
- count = qemu_input_key_value_to_scancode(evt->key->key,
- evt->key->down,
+ count = qemu_input_key_value_to_scancode(key->key,
+ key->down,
scancodes);
for (i = 0; i < count; i++) {
entry->put_kbd(entry->opaque, scancodes[i]);
@@ -143,28 +145,31 @@ QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
InputEvent *evt)
{
- static const int bmap[INPUT_BUTTON_MAX] = {
+ static const int bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
};
QEMUPutMouseEntry *s = (QEMUPutMouseEntry *)dev;
+ InputBtnEvent *btn;
+ InputMoveEvent *move;
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_BTN:
- if (evt->btn->down) {
- s->buttons |= bmap[evt->btn->button];
+ btn = evt->u.btn.data;
+ if (btn->down) {
+ s->buttons |= bmap[btn->button];
} else {
- s->buttons &= ~bmap[evt->btn->button];
+ s->buttons &= ~bmap[btn->button];
}
- if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+ if (btn->down && btn->button == INPUT_BUTTON_WHEEL_UP) {
s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
s->axis[INPUT_AXIS_X],
s->axis[INPUT_AXIS_Y],
-1,
s->buttons);
}
- if (evt->btn->down && evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+ if (btn->down && btn->button == INPUT_BUTTON_WHEEL_DOWN) {
s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
s->axis[INPUT_AXIS_X],
s->axis[INPUT_AXIS_Y],
@@ -173,10 +178,12 @@ static void legacy_mouse_event(DeviceState *dev, QemuConsole *src,
}
break;
case INPUT_EVENT_KIND_ABS:
- s->axis[evt->abs->axis] = evt->abs->value;
+ move = evt->u.abs.data;
+ s->axis[move->axis] = move->value;
break;
case INPUT_EVENT_KIND_REL:
- s->axis[evt->rel->axis] += evt->rel->value;
+ move = evt->u.rel.data;
+ s->axis[move->axis] += move->value;
break;
default:
break;
@@ -205,7 +212,7 @@ QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
{
QEMUPutMouseEntry *s;
- s = g_malloc0(sizeof(QEMUPutMouseEntry));
+ s = g_new0(QEMUPutMouseEntry, 1);
s->qemu_put_mouse_event = func;
s->qemu_put_mouse_event_opaque = opaque;
@@ -239,7 +246,7 @@ QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func,
{
QEMUPutLEDEntry *s;
- s = g_malloc0(sizeof(QEMUPutLEDEntry));
+ s = g_new0(QEMUPutLEDEntry, 1);
s->put_led = func;
s->opaque = opaque;
diff --git a/qemu/ui/input-linux.c b/qemu/ui/input-linux.c
new file mode 100644
index 000000000..1d33b5c12
--- /dev/null
+++ b/qemu/ui/input-linux.c
@@ -0,0 +1,507 @@
+/*
+ * 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 "qapi/error.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "qemu/sockets.h"
+#include "sysemu/sysemu.h"
+#include "ui/input.h"
+#include "qom/object_interfaces.h"
+
+#include <sys/ioctl.h>
+#include "standard-headers/linux/input.h"
+
+static int linux_to_qcode[KEY_CNT] = {
+ [KEY_ESC] = Q_KEY_CODE_ESC,
+ [KEY_1] = Q_KEY_CODE_1,
+ [KEY_2] = Q_KEY_CODE_2,
+ [KEY_3] = Q_KEY_CODE_3,
+ [KEY_4] = Q_KEY_CODE_4,
+ [KEY_5] = Q_KEY_CODE_5,
+ [KEY_6] = Q_KEY_CODE_6,
+ [KEY_7] = Q_KEY_CODE_7,
+ [KEY_8] = Q_KEY_CODE_8,
+ [KEY_9] = Q_KEY_CODE_9,
+ [KEY_0] = Q_KEY_CODE_0,
+ [KEY_MINUS] = Q_KEY_CODE_MINUS,
+ [KEY_EQUAL] = Q_KEY_CODE_EQUAL,
+ [KEY_BACKSPACE] = Q_KEY_CODE_BACKSPACE,
+ [KEY_TAB] = Q_KEY_CODE_TAB,
+ [KEY_Q] = Q_KEY_CODE_Q,
+ [KEY_W] = Q_KEY_CODE_W,
+ [KEY_E] = Q_KEY_CODE_E,
+ [KEY_R] = Q_KEY_CODE_R,
+ [KEY_T] = Q_KEY_CODE_T,
+ [KEY_Y] = Q_KEY_CODE_Y,
+ [KEY_U] = Q_KEY_CODE_U,
+ [KEY_I] = Q_KEY_CODE_I,
+ [KEY_O] = Q_KEY_CODE_O,
+ [KEY_P] = Q_KEY_CODE_P,
+ [KEY_LEFTBRACE] = Q_KEY_CODE_BRACKET_LEFT,
+ [KEY_RIGHTBRACE] = Q_KEY_CODE_BRACKET_RIGHT,
+ [KEY_ENTER] = Q_KEY_CODE_RET,
+ [KEY_LEFTCTRL] = Q_KEY_CODE_CTRL,
+ [KEY_A] = Q_KEY_CODE_A,
+ [KEY_S] = Q_KEY_CODE_S,
+ [KEY_D] = Q_KEY_CODE_D,
+ [KEY_F] = Q_KEY_CODE_F,
+ [KEY_G] = Q_KEY_CODE_G,
+ [KEY_H] = Q_KEY_CODE_H,
+ [KEY_J] = Q_KEY_CODE_J,
+ [KEY_K] = Q_KEY_CODE_K,
+ [KEY_L] = Q_KEY_CODE_L,
+ [KEY_SEMICOLON] = Q_KEY_CODE_SEMICOLON,
+ [KEY_APOSTROPHE] = Q_KEY_CODE_APOSTROPHE,
+ [KEY_GRAVE] = Q_KEY_CODE_GRAVE_ACCENT,
+ [KEY_LEFTSHIFT] = Q_KEY_CODE_SHIFT,
+ [KEY_BACKSLASH] = Q_KEY_CODE_BACKSLASH,
+ [KEY_102ND] = Q_KEY_CODE_LESS,
+ [KEY_Z] = Q_KEY_CODE_Z,
+ [KEY_X] = Q_KEY_CODE_X,
+ [KEY_C] = Q_KEY_CODE_C,
+ [KEY_V] = Q_KEY_CODE_V,
+ [KEY_B] = Q_KEY_CODE_B,
+ [KEY_N] = Q_KEY_CODE_N,
+ [KEY_M] = Q_KEY_CODE_M,
+ [KEY_COMMA] = Q_KEY_CODE_COMMA,
+ [KEY_DOT] = Q_KEY_CODE_DOT,
+ [KEY_SLASH] = Q_KEY_CODE_SLASH,
+ [KEY_RIGHTSHIFT] = Q_KEY_CODE_SHIFT_R,
+ [KEY_LEFTALT] = Q_KEY_CODE_ALT,
+ [KEY_SPACE] = Q_KEY_CODE_SPC,
+ [KEY_CAPSLOCK] = Q_KEY_CODE_CAPS_LOCK,
+ [KEY_F1] = Q_KEY_CODE_F1,
+ [KEY_F2] = Q_KEY_CODE_F2,
+ [KEY_F3] = Q_KEY_CODE_F3,
+ [KEY_F4] = Q_KEY_CODE_F4,
+ [KEY_F5] = Q_KEY_CODE_F5,
+ [KEY_F6] = Q_KEY_CODE_F6,
+ [KEY_F7] = Q_KEY_CODE_F7,
+ [KEY_F8] = Q_KEY_CODE_F8,
+ [KEY_F9] = Q_KEY_CODE_F9,
+ [KEY_F10] = Q_KEY_CODE_F10,
+ [KEY_NUMLOCK] = Q_KEY_CODE_NUM_LOCK,
+ [KEY_SCROLLLOCK] = Q_KEY_CODE_SCROLL_LOCK,
+ [KEY_KP0] = Q_KEY_CODE_KP_0,
+ [KEY_KP1] = Q_KEY_CODE_KP_1,
+ [KEY_KP2] = Q_KEY_CODE_KP_2,
+ [KEY_KP3] = Q_KEY_CODE_KP_3,
+ [KEY_KP4] = Q_KEY_CODE_KP_4,
+ [KEY_KP5] = Q_KEY_CODE_KP_5,
+ [KEY_KP6] = Q_KEY_CODE_KP_6,
+ [KEY_KP7] = Q_KEY_CODE_KP_7,
+ [KEY_KP8] = Q_KEY_CODE_KP_8,
+ [KEY_KP9] = Q_KEY_CODE_KP_9,
+ [KEY_KPMINUS] = Q_KEY_CODE_KP_SUBTRACT,
+ [KEY_KPPLUS] = Q_KEY_CODE_KP_ADD,
+ [KEY_KPDOT] = Q_KEY_CODE_KP_DECIMAL,
+ [KEY_KPENTER] = Q_KEY_CODE_KP_ENTER,
+ [KEY_KPSLASH] = Q_KEY_CODE_KP_DIVIDE,
+ [KEY_KPASTERISK] = Q_KEY_CODE_KP_MULTIPLY,
+ [KEY_F11] = Q_KEY_CODE_F11,
+ [KEY_F12] = Q_KEY_CODE_F12,
+ [KEY_RIGHTCTRL] = Q_KEY_CODE_CTRL_R,
+ [KEY_SYSRQ] = Q_KEY_CODE_SYSRQ,
+ [KEY_RIGHTALT] = Q_KEY_CODE_ALT_R,
+ [KEY_HOME] = Q_KEY_CODE_HOME,
+ [KEY_UP] = Q_KEY_CODE_UP,
+ [KEY_PAGEUP] = Q_KEY_CODE_PGUP,
+ [KEY_LEFT] = Q_KEY_CODE_LEFT,
+ [KEY_RIGHT] = Q_KEY_CODE_RIGHT,
+ [KEY_END] = Q_KEY_CODE_END,
+ [KEY_DOWN] = Q_KEY_CODE_DOWN,
+ [KEY_PAGEDOWN] = Q_KEY_CODE_PGDN,
+ [KEY_INSERT] = Q_KEY_CODE_INSERT,
+ [KEY_DELETE] = Q_KEY_CODE_DELETE,
+ [KEY_LEFTMETA] = Q_KEY_CODE_META_L,
+ [KEY_RIGHTMETA] = Q_KEY_CODE_META_R,
+ [KEY_MENU] = Q_KEY_CODE_MENU,
+};
+
+static int qemu_input_linux_to_qcode(unsigned int lnx)
+{
+ assert(lnx < KEY_CNT);
+ return linux_to_qcode[lnx];
+}
+
+#define TYPE_INPUT_LINUX "input-linux"
+#define INPUT_LINUX(obj) \
+ OBJECT_CHECK(InputLinux, (obj), TYPE_INPUT_LINUX)
+#define INPUT_LINUX_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(InputLinuxClass, (obj), TYPE_INPUT_LINUX)
+#define INPUT_LINUX_CLASS(klass) \
+ OBJECT_CLASS_CHECK(InputLinuxClass, (klass), TYPE_INPUT_LINUX)
+
+typedef struct InputLinux InputLinux;
+typedef struct InputLinuxClass InputLinuxClass;
+
+struct InputLinux {
+ Object parent;
+
+ char *evdev;
+ int fd;
+ bool repeat;
+ bool grab_request;
+ bool grab_active;
+ bool grab_all;
+ bool keydown[KEY_CNT];
+ int keycount;
+ int wheel;
+ bool initialized;
+ QTAILQ_ENTRY(InputLinux) next;
+};
+
+struct InputLinuxClass {
+ ObjectClass parent_class;
+};
+
+static QTAILQ_HEAD(, InputLinux) inputs = QTAILQ_HEAD_INITIALIZER(inputs);
+
+static void input_linux_toggle_grab(InputLinux *il)
+{
+ intptr_t request = !il->grab_active;
+ InputLinux *item;
+ int rc;
+
+ rc = ioctl(il->fd, EVIOCGRAB, request);
+ if (rc < 0) {
+ return;
+ }
+ il->grab_active = !il->grab_active;
+
+ if (!il->grab_all) {
+ return;
+ }
+ QTAILQ_FOREACH(item, &inputs, next) {
+ if (item == il || item->grab_all) {
+ /* avoid endless loops */
+ continue;
+ }
+ if (item->grab_active != il->grab_active) {
+ input_linux_toggle_grab(item);
+ }
+ }
+}
+
+static void input_linux_event_keyboard(void *opaque)
+{
+ InputLinux *il = opaque;
+ struct input_event event;
+ int rc;
+
+ for (;;) {
+ rc = read(il->fd, &event, sizeof(event));
+ if (rc != sizeof(event)) {
+ if (rc < 0 && errno != EAGAIN) {
+ fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
+ qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
+ close(il->fd);
+ }
+ break;
+ }
+
+ switch (event.type) {
+ case EV_KEY:
+ if (event.value > 2 || (event.value > 1 && !il->repeat)) {
+ /*
+ * ignore autorepeat + unknown key events
+ * 0 == up, 1 == down, 2 == autorepeat, other == undefined
+ */
+ continue;
+ }
+ if (event.code >= KEY_CNT) {
+ /*
+ * Should not happen. But better safe than sorry,
+ * and we make Coverity happy too.
+ */
+ continue;
+ }
+ /* keep track of key state */
+ if (!il->keydown[event.code] && event.value) {
+ il->keydown[event.code] = true;
+ il->keycount++;
+ }
+ if (il->keydown[event.code] && !event.value) {
+ il->keydown[event.code] = false;
+ il->keycount--;
+ }
+
+ /* send event to guest when grab is active */
+ if (il->grab_active) {
+ int qcode = qemu_input_linux_to_qcode(event.code);
+ qemu_input_event_send_key_qcode(NULL, qcode, event.value);
+ }
+
+ /* hotkey -> record switch request ... */
+ if (il->keydown[KEY_LEFTCTRL] &&
+ il->keydown[KEY_RIGHTCTRL]) {
+ il->grab_request = true;
+ }
+
+ /*
+ * ... and do the switch when all keys are lifted, so we
+ * confuse neither guest nor host with keys which seem to
+ * be stuck due to missing key-up events.
+ */
+ if (il->grab_request && !il->keycount) {
+ il->grab_request = false;
+ input_linux_toggle_grab(il);
+ }
+ break;
+ }
+ }
+}
+
+static void input_linux_event_mouse_button(int button)
+{
+ qemu_input_queue_btn(NULL, button, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(NULL, button, false);
+ qemu_input_event_sync();
+}
+
+static void input_linux_event_mouse(void *opaque)
+{
+ InputLinux *il = opaque;
+ struct input_event event;
+ int rc;
+
+ for (;;) {
+ rc = read(il->fd, &event, sizeof(event));
+ if (rc != sizeof(event)) {
+ if (rc < 0 && errno != EAGAIN) {
+ fprintf(stderr, "%s: read: %s\n", __func__, strerror(errno));
+ qemu_set_fd_handler(il->fd, NULL, NULL, NULL);
+ close(il->fd);
+ }
+ break;
+ }
+
+ /* only send event to guest when grab is active */
+ if (!il->grab_active) {
+ continue;
+ }
+
+ switch (event.type) {
+ case EV_KEY:
+ switch (event.code) {
+ case BTN_LEFT:
+ qemu_input_queue_btn(NULL, INPUT_BUTTON_LEFT, event.value);
+ break;
+ case BTN_RIGHT:
+ qemu_input_queue_btn(NULL, INPUT_BUTTON_RIGHT, event.value);
+ break;
+ case BTN_MIDDLE:
+ qemu_input_queue_btn(NULL, INPUT_BUTTON_MIDDLE, event.value);
+ break;
+ case BTN_GEAR_UP:
+ qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_UP, event.value);
+ break;
+ case BTN_GEAR_DOWN:
+ qemu_input_queue_btn(NULL, INPUT_BUTTON_WHEEL_DOWN,
+ event.value);
+ break;
+ };
+ break;
+ case EV_REL:
+ switch (event.code) {
+ case REL_X:
+ qemu_input_queue_rel(NULL, INPUT_AXIS_X, event.value);
+ break;
+ case REL_Y:
+ qemu_input_queue_rel(NULL, INPUT_AXIS_Y, event.value);
+ break;
+ case REL_WHEEL:
+ il->wheel = event.value;
+ break;
+ }
+ break;
+ case EV_SYN:
+ qemu_input_event_sync();
+ if (il->wheel != 0) {
+ input_linux_event_mouse_button((il->wheel > 0)
+ ? INPUT_BUTTON_WHEEL_UP
+ : INPUT_BUTTON_WHEEL_DOWN);
+ il->wheel = 0;
+ }
+ break;
+ }
+ }
+}
+
+static void input_linux_complete(UserCreatable *uc, Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(uc);
+ uint32_t evtmap, relmap, absmap;
+ int rc, ver;
+
+ if (!il->evdev) {
+ error_setg(errp, "no input device specified");
+ return;
+ }
+
+ il->fd = open(il->evdev, O_RDWR);
+ if (il->fd < 0) {
+ error_setg_file_open(errp, errno, il->evdev);
+ return;
+ }
+ qemu_set_nonblock(il->fd);
+
+ rc = ioctl(il->fd, EVIOCGVERSION, &ver);
+ if (rc < 0) {
+ error_setg(errp, "%s: is not an evdev device", il->evdev);
+ goto err_close;
+ }
+
+ rc = ioctl(il->fd, EVIOCGBIT(0, sizeof(evtmap)), &evtmap);
+ if (rc < 0) {
+ error_setg(errp, "%s: failed to read event bits", il->evdev);
+ goto err_close;
+ }
+
+ if (evtmap & (1 << EV_REL)) {
+ rc = ioctl(il->fd, EVIOCGBIT(EV_REL, sizeof(relmap)), &relmap);
+ if (rc < 0) {
+ relmap = 0;
+ }
+ }
+
+ if (evtmap & (1 << EV_ABS)) {
+ ioctl(il->fd, EVIOCGBIT(EV_ABS, sizeof(absmap)), &absmap);
+ if (rc < 0) {
+ absmap = 0;
+ }
+ }
+
+ if ((evtmap & (1 << EV_REL)) &&
+ (relmap & (1 << REL_X))) {
+ /* has relative x axis -> assume mouse */
+ qemu_set_fd_handler(il->fd, input_linux_event_mouse, NULL, il);
+ } else if ((evtmap & (1 << EV_ABS)) &&
+ (absmap & (1 << ABS_X))) {
+ /* has absolute x axis -> not supported */
+ error_setg(errp, "tablet/touchscreen not supported");
+ goto err_close;
+ } else if (evtmap & (1 << EV_KEY)) {
+ /* has keys/buttons (and no x axis) -> assume keyboard */
+ qemu_set_fd_handler(il->fd, input_linux_event_keyboard, NULL, il);
+ } else {
+ /* Huh? What is this? */
+ error_setg(errp, "unknown kind of input device");
+ goto err_close;
+ }
+ input_linux_toggle_grab(il);
+ QTAILQ_INSERT_TAIL(&inputs, il, next);
+ il->initialized = true;
+ return;
+
+err_close:
+ close(il->fd);
+ return;
+}
+
+static void input_linux_instance_finalize(Object *obj)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ if (il->initialized) {
+ QTAILQ_REMOVE(&inputs, il, next);
+ close(il->fd);
+ }
+ g_free(il->evdev);
+}
+
+static char *input_linux_get_evdev(Object *obj, Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ return g_strdup(il->evdev);
+}
+
+static void input_linux_set_evdev(Object *obj, const char *value,
+ Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ if (il->evdev) {
+ error_setg(errp, "evdev property already set");
+ return;
+ }
+ il->evdev = g_strdup(value);
+}
+
+static bool input_linux_get_grab_all(Object *obj, Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ return il->grab_all;
+}
+
+static void input_linux_set_grab_all(Object *obj, bool value,
+ Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ il->grab_all = value;
+}
+
+static bool input_linux_get_repeat(Object *obj, Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ return il->repeat;
+}
+
+static void input_linux_set_repeat(Object *obj, bool value,
+ Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ il->repeat = value;
+}
+
+static void input_linux_instance_init(Object *obj)
+{
+ object_property_add_str(obj, "evdev",
+ input_linux_get_evdev,
+ input_linux_set_evdev, NULL);
+ object_property_add_bool(obj, "grab_all",
+ input_linux_get_grab_all,
+ input_linux_set_grab_all, NULL);
+ object_property_add_bool(obj, "repeat",
+ input_linux_get_repeat,
+ input_linux_set_repeat, NULL);
+}
+
+static void input_linux_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = input_linux_complete;
+}
+
+static const TypeInfo input_linux_info = {
+ .name = TYPE_INPUT_LINUX,
+ .parent = TYPE_OBJECT,
+ .class_size = sizeof(InputLinuxClass),
+ .class_init = input_linux_class_init,
+ .instance_size = sizeof(InputLinux),
+ .instance_init = input_linux_instance_init,
+ .instance_finalize = input_linux_instance_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+static void register_types(void)
+{
+ type_register_static(&input_linux_info);
+}
+
+type_init(register_types);
diff --git a/qemu/ui/input.c b/qemu/ui/input.c
index 1a552d1de..ed88cda6d 100644
--- a/qemu/ui/input.c
+++ b/qemu/ui/input.c
@@ -1,3 +1,4 @@
+#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "sysemu/sysemu.h"
#include "qapi-types.h"
@@ -6,6 +7,7 @@
#include "trace.h"
#include "ui/input.h"
#include "ui/console.h"
+#include "sysemu/replay.h"
struct QemuInputHandlerState {
DeviceState *dev;
@@ -80,19 +82,12 @@ void qemu_input_handler_bind(QemuInputHandlerState *s,
const char *device_id, int head,
Error **errp)
{
- DeviceState *dev;
QemuConsole *con;
+ Error *err = NULL;
- dev = qdev_find_recursive(sysbus_get_default(), device_id);
- if (dev == NULL) {
- error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
- "Device '%s' not found", device_id);
- return;
- }
-
- con = qemu_console_lookup_by_device(dev, head);
- if (con == NULL) {
- error_setg(errp, "Device %s is not bound to a QemuConsole", device_id);
+ con = qemu_console_lookup_by_device_name(device_id, head, &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
@@ -124,17 +119,22 @@ qemu_input_find_handler(uint32_t mask, QemuConsole *con)
return NULL;
}
-void qmp_x_input_send_event(bool has_console, int64_t console,
- InputEventList *events, Error **errp)
+void qmp_input_send_event(bool has_device, const char *device,
+ bool has_head, int64_t head,
+ InputEventList *events, Error **errp)
{
InputEventList *e;
QemuConsole *con;
+ Error *err = NULL;
con = NULL;
- if (has_console) {
- con = qemu_console_lookup_by_index(console);
- if (!con) {
- error_setg(errp, "console %" PRId64 " not found", console);
+ if (has_device) {
+ if (!has_head) {
+ head = 0;
+ }
+ con = qemu_console_lookup_by_device_name(device, head, &err);
+ if (err) {
+ error_propagate(errp, err);
return;
}
}
@@ -147,10 +147,10 @@ void qmp_x_input_send_event(bool has_console, int64_t console,
for (e = events; e != NULL; e = e->next) {
InputEvent *event = e->value;
- if (!qemu_input_find_handler(1 << event->kind, con)) {
+ if (!qemu_input_find_handler(1 << event->type, con)) {
error_setg(errp, "Input handler not found for "
"event type %s",
- InputEventKind_lookup[event->kind]);
+ InputEventKind_lookup[event->type]);
return;
}
}
@@ -166,24 +166,25 @@ void qmp_x_input_send_event(bool has_console, int64_t console,
static void qemu_input_transform_abs_rotate(InputEvent *evt)
{
+ InputMoveEvent *move = evt->u.abs.data;
switch (graphic_rotate) {
case 90:
- if (evt->abs->axis == INPUT_AXIS_X) {
- evt->abs->axis = INPUT_AXIS_Y;
- } else if (evt->abs->axis == INPUT_AXIS_Y) {
- evt->abs->axis = INPUT_AXIS_X;
- evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+ if (move->axis == INPUT_AXIS_X) {
+ move->axis = INPUT_AXIS_Y;
+ } else if (move->axis == INPUT_AXIS_Y) {
+ move->axis = INPUT_AXIS_X;
+ move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
}
break;
case 180:
- evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
+ move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
break;
case 270:
- if (evt->abs->axis == INPUT_AXIS_X) {
- evt->abs->axis = INPUT_AXIS_Y;
- evt->abs->value = INPUT_EVENT_ABS_SIZE - 1 - evt->abs->value;
- } else if (evt->abs->axis == INPUT_AXIS_Y) {
- evt->abs->axis = INPUT_AXIS_X;
+ if (move->axis == INPUT_AXIS_X) {
+ move->axis = INPUT_AXIS_Y;
+ move->value = INPUT_EVENT_ABS_SIZE - 1 - move->value;
+ } else if (move->axis == INPUT_AXIS_Y) {
+ move->axis = INPUT_AXIS_X;
}
break;
}
@@ -193,41 +194,48 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
{
const char *name;
int qcode, idx = -1;
+ InputKeyEvent *key;
+ InputBtnEvent *btn;
+ InputMoveEvent *move;
if (src) {
idx = qemu_console_get_index(src);
}
- switch (evt->kind) {
+ switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
- switch (evt->key->key->kind) {
+ key = evt->u.key.data;
+ switch (key->key->type) {
case KEY_VALUE_KIND_NUMBER:
- qcode = qemu_input_key_number_to_qcode(evt->key->key->number);
+ qcode = qemu_input_key_number_to_qcode(key->key->u.number.data);
name = QKeyCode_lookup[qcode];
- trace_input_event_key_number(idx, evt->key->key->number,
- name, evt->key->down);
+ trace_input_event_key_number(idx, key->key->u.number.data,
+ name, key->down);
break;
case KEY_VALUE_KIND_QCODE:
- name = QKeyCode_lookup[evt->key->key->qcode];
- trace_input_event_key_qcode(idx, name, evt->key->down);
+ name = QKeyCode_lookup[key->key->u.qcode.data];
+ trace_input_event_key_qcode(idx, name, key->down);
break;
- case KEY_VALUE_KIND_MAX:
+ case KEY_VALUE_KIND__MAX:
/* keep gcc happy */
break;
}
break;
case INPUT_EVENT_KIND_BTN:
- name = InputButton_lookup[evt->btn->button];
- trace_input_event_btn(idx, name, evt->btn->down);
+ btn = evt->u.btn.data;
+ name = InputButton_lookup[btn->button];
+ trace_input_event_btn(idx, name, btn->down);
break;
case INPUT_EVENT_KIND_REL:
- name = InputAxis_lookup[evt->rel->axis];
- trace_input_event_rel(idx, name, evt->rel->value);
+ move = evt->u.rel.data;
+ name = InputAxis_lookup[move->axis];
+ trace_input_event_rel(idx, name, move->value);
break;
case INPUT_EVENT_KIND_ABS:
- name = InputAxis_lookup[evt->abs->axis];
- trace_input_event_abs(idx, name, evt->abs->value);
+ move = evt->u.abs.data;
+ name = InputAxis_lookup[move->axis];
+ trace_input_event_abs(idx, name, move->value);
break;
- case INPUT_EVENT_KIND_MAX:
+ case INPUT_EVENT_KIND__MAX:
/* keep gcc happy */
break;
}
@@ -300,23 +308,19 @@ static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue)
QTAILQ_INSERT_TAIL(queue, item, node);
}
-void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
+void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt)
{
QemuInputHandlerState *s;
- if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
- return;
- }
-
qemu_input_event_trace(src, evt);
/* pre processing */
- if (graphic_rotate && (evt->kind == INPUT_EVENT_KIND_ABS)) {
+ if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) {
qemu_input_transform_abs_rotate(evt);
}
/* send event */
- s = qemu_input_find_handler(1 << evt->kind, src);
+ s = qemu_input_find_handler(1 << evt->type, src);
if (!s) {
return;
}
@@ -324,14 +328,19 @@ void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
s->events++;
}
-void qemu_input_event_sync(void)
+void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
{
- QemuInputHandlerState *s;
-
if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
return;
}
+ replay_input_event(src, evt);
+}
+
+void qemu_input_event_sync_impl(void)
+{
+ QemuInputHandlerState *s;
+
trace_input_event_sync();
QTAILQ_FOREACH(s, &handlers, node) {
@@ -345,13 +354,22 @@ void qemu_input_event_sync(void)
}
}
+void qemu_input_event_sync(void)
+{
+ if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) {
+ return;
+ }
+
+ replay_input_sync_event();
+}
+
InputEvent *qemu_input_event_new_key(KeyValue *key, bool down)
{
InputEvent *evt = g_new0(InputEvent, 1);
- evt->key = g_new0(InputKeyEvent, 1);
- evt->kind = INPUT_EVENT_KIND_KEY;
- evt->key->key = key;
- evt->key->down = down;
+ evt->u.key.data = g_new0(InputKeyEvent, 1);
+ evt->type = INPUT_EVENT_KIND_KEY;
+ evt->u.key.data->key = key;
+ evt->u.key.data->down = down;
return evt;
}
@@ -372,16 +390,16 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
{
KeyValue *key = g_new0(KeyValue, 1);
- key->kind = KEY_VALUE_KIND_NUMBER;
- key->number = num;
+ key->type = KEY_VALUE_KIND_NUMBER;
+ key->u.number.data = num;
qemu_input_event_send_key(src, key, down);
}
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
{
KeyValue *key = g_new0(KeyValue, 1);
- key->kind = KEY_VALUE_KIND_QCODE;
- key->qcode = q;
+ key->type = KEY_VALUE_KIND_QCODE;
+ key->u.qcode.data = q;
qemu_input_event_send_key(src, key, down);
}
@@ -398,10 +416,10 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms)
InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
{
InputEvent *evt = g_new0(InputEvent, 1);
- evt->btn = g_new0(InputBtnEvent, 1);
- evt->kind = INPUT_EVENT_KIND_BTN;
- evt->btn->button = btn;
- evt->btn->down = down;
+ evt->u.btn.data = g_new0(InputBtnEvent, 1);
+ evt->type = INPUT_EVENT_KIND_BTN;
+ evt->u.btn.data->button = btn;
+ evt->u.btn.data->down = down;
return evt;
}
@@ -419,7 +437,7 @@ void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map,
InputButton btn;
uint32_t mask;
- for (btn = 0; btn < INPUT_BUTTON_MAX; btn++) {
+ for (btn = 0; btn < INPUT_BUTTON__MAX; btn++) {
mask = button_map[btn];
if ((button_old & mask) == (button_new & mask)) {
continue;
@@ -451,8 +469,8 @@ InputEvent *qemu_input_event_new_move(InputEventKind kind,
InputEvent *evt = g_new0(InputEvent, 1);
InputMoveEvent *move = g_new0(InputMoveEvent, 1);
- evt->kind = kind;
- evt->data = move;
+ evt->type = kind;
+ evt->u.rel.data = move; /* evt->u.rel is the same as evt->u.abs */
move->axis = axis;
move->value = value;
return evt;
diff --git a/qemu/ui/keymaps.c b/qemu/ui/keymaps.c
index 49410ae9d..8899a0b31 100644
--- a/qemu/ui/keymaps.c
+++ b/qemu/ui/keymaps.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "keymaps.h"
#include "sysemu/sysemu.h"
@@ -109,7 +110,7 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
}
if (!k) {
- k = g_malloc0(sizeof(kbd_layout_t));
+ k = g_new0(kbd_layout_t, 1);
}
for(;;) {
diff --git a/qemu/ui/qemu-pixman.c b/qemu/ui/qemu-pixman.c
index 4116e1507..c9f8dce7f 100644
--- a/qemu/ui/qemu-pixman.c
+++ b/qemu/ui/qemu-pixman.c
@@ -3,6 +3,7 @@
* See the COPYING file in the top-level directory.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
diff --git a/qemu/ui/sdl.c b/qemu/ui/sdl.c
index 3be29101e..d8cf5bcf7 100644
--- a/qemu/ui/sdl.c
+++ b/qemu/ui/sdl.c
@@ -25,10 +25,12 @@
/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
#undef WIN32_LEAN_AND_MEAN
+#include "qemu/osdep.h"
#include <SDL.h>
#include <SDL_syswm.h>
#include "qemu-common.h"
+#include "qemu/cutils.h"
#include "ui/console.h"
#include "ui/input.h"
#include "sysemu/sysemu.h"
@@ -60,6 +62,11 @@ static SDL_Cursor *guest_sprite = NULL;
static SDL_PixelFormat host_format;
static int scaling_active = 0;
static Notifier mouse_mode_notifier;
+static int idle_counter;
+
+#define SDL_REFRESH_INTERVAL_BUSY 10
+#define SDL_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \
+ / SDL_REFRESH_INTERVAL_BUSY + 1)
#if 0
#define DEBUG_SDL
@@ -465,7 +472,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
{
- static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
[INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
[INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
@@ -802,6 +809,7 @@ static void handle_activation(SDL_Event *ev)
static void sdl_refresh(DisplayChangeListener *dcl)
{
SDL_Event ev1, *ev = &ev1;
+ int idle = 1;
if (last_vm_running != runstate_is_running()) {
last_vm_running = runstate_is_running();
@@ -817,9 +825,11 @@ static void sdl_refresh(DisplayChangeListener *dcl)
sdl_update(dcl, 0, 0, real_screen->w, real_screen->h);
break;
case SDL_KEYDOWN:
+ idle = 0;
handle_keydown(ev);
break;
case SDL_KEYUP:
+ idle = 0;
handle_keyup(ev);
break;
case SDL_QUIT:
@@ -829,10 +839,12 @@ static void sdl_refresh(DisplayChangeListener *dcl)
}
break;
case SDL_MOUSEMOTION:
+ idle = 0;
handle_mousemotion(ev);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
+ idle = 0;
handle_mousebutton(ev);
break;
case SDL_ACTIVEEVENT:
@@ -847,6 +859,18 @@ static void sdl_refresh(DisplayChangeListener *dcl)
break;
}
}
+
+ if (idle) {
+ if (idle_counter < SDL_MAX_IDLE_COUNT) {
+ idle_counter++;
+ if (idle_counter >= SDL_MAX_IDLE_COUNT) {
+ dcl->update_interval = GUI_REFRESH_INTERVAL_DEFAULT;
+ }
+ }
+ } else {
+ idle_counter = 0;
+ dcl->update_interval = SDL_REFRESH_INTERVAL_BUSY;
+ }
}
static void sdl_mouse_warp(DisplayChangeListener *dcl,
@@ -985,7 +1009,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
sdl_grab_start();
}
- dcl = g_malloc0(sizeof(DisplayChangeListener));
+ dcl = g_new0(DisplayChangeListener, 1);
dcl->ops = &dcl_ops;
register_displaychangelistener(dcl);
diff --git a/qemu/ui/sdl2-2d.c b/qemu/ui/sdl2-2d.c
index d0b340f95..95930061e 100644
--- a/qemu/ui/sdl2-2d.c
+++ b/qemu/ui/sdl2-2d.c
@@ -23,6 +23,7 @@
*/
/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
@@ -45,10 +46,23 @@ void sdl2_2d_update(DisplayChangeListener *dcl,
return;
}
+ /*
+ * SDL2 seems to do some double-buffering, and trying to only
+ * update the changed areas results in only one of the two buffers
+ * being updated. Which flickers alot. So lets not try to be
+ * clever do a full update every time ...
+ */
+#if 0
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
+#else
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = surface_width(surf);
+ rect.h = surface_height(surf);
+#endif
SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
surface_stride(surf));
diff --git a/qemu/ui/sdl2-gl.c b/qemu/ui/sdl2-gl.c
index b604c0671..a324ecaca 100644
--- a/qemu/ui/sdl2-gl.c
+++ b/qemu/ui/sdl2-gl.c
@@ -25,17 +25,44 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
#include "ui/sdl2.h"
#include "sysemu/sysemu.h"
+#include <epoxy/gl.h>
+
+static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout)
+{
+ if (scon->scanout_mode == scanout) {
+ return;
+ }
+
+ scon->scanout_mode = scanout;
+ if (!scon->scanout_mode) {
+ if (scon->fbo_id) {
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, 0, 0);
+ glDeleteFramebuffers(1, &scon->fbo_id);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ scon->fbo_id = 0;
+ }
+ if (scon->surface) {
+ surface_gl_destroy_texture(scon->gls, scon->surface);
+ surface_gl_create_texture(scon->gls, scon->surface);
+ }
+ }
+}
+
static void sdl2_gl_render_surface(struct sdl2_console *scon)
{
int ww, wh;
SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
+ sdl2_set_scanout_mode(scon, false);
SDL_GetWindowSize(scon->real_window, &ww, &wh);
surface_gl_setup_viewport(scon->gls, scon->surface, ww, wh);
@@ -110,3 +137,112 @@ void sdl2_gl_redraw(struct sdl2_console *scon)
sdl2_gl_render_surface(scon);
}
}
+
+QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+ SDL_GLContext ctx;
+
+ assert(scon->opengl);
+
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
+
+ SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
+ SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver);
+
+ ctx = SDL_GL_CreateContext(scon->real_window);
+ return (QEMUGLContext)ctx;
+}
+
+void sdl2_gl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ SDL_GLContext sdlctx = (SDL_GLContext)ctx;
+
+ SDL_GL_DeleteContext(sdlctx);
+}
+
+int sdl2_gl_make_context_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+ SDL_GLContext sdlctx = (SDL_GLContext)ctx;
+
+ assert(scon->opengl);
+
+ return SDL_GL_MakeCurrent(scon->real_window, sdlctx);
+}
+
+QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl)
+{
+ SDL_GLContext sdlctx;
+
+ sdlctx = SDL_GL_GetCurrentContext();
+ return (QEMUGLContext)sdlctx;
+}
+
+void sdl2_gl_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+
+ assert(scon->opengl);
+ scon->x = x;
+ scon->y = y;
+ scon->w = w;
+ scon->h = h;
+ scon->tex_id = backing_id;
+ scon->y0_top = backing_y_0_top;
+
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
+
+ if (scon->tex_id == 0 || scon->w == 0 || scon->h == 0) {
+ sdl2_set_scanout_mode(scon, false);
+ return;
+ }
+
+ sdl2_set_scanout_mode(scon, true);
+ if (!scon->fbo_id) {
+ glGenFramebuffers(1, &scon->fbo_id);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, scon->tex_id, 0);
+}
+
+void sdl2_gl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl);
+ int ww, wh, y1, y2;
+
+ assert(scon->opengl);
+ if (!scon->scanout_mode) {
+ return;
+ }
+ if (!scon->fbo_id) {
+ return;
+ }
+
+ SDL_GL_MakeCurrent(scon->real_window, scon->winctx);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, scon->fbo_id);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+ SDL_GetWindowSize(scon->real_window, &ww, &wh);
+ glViewport(0, 0, ww, wh);
+ y1 = scon->y0_top ? 0 : scon->h;
+ y2 = scon->y0_top ? scon->h : 0;
+ glBlitFramebuffer(0, y1, scon->w, y2,
+ 0, 0, ww, wh,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, scon->fbo_id);
+
+ SDL_GL_SwapWindow(scon->real_window);
+}
diff --git a/qemu/ui/sdl2-input.c b/qemu/ui/sdl2-input.c
index ac5dc9476..6e315ae80 100644
--- a/qemu/ui/sdl2-input.c
+++ b/qemu/ui/sdl2-input.c
@@ -23,6 +23,7 @@
*/
/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
diff --git a/qemu/ui/sdl2.c b/qemu/ui/sdl2.c
index 5cb75aa36..d0424421e 100644
--- a/qemu/ui/sdl2.c
+++ b/qemu/ui/sdl2.c
@@ -23,6 +23,7 @@
*/
/* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/console.h"
#include "ui/input.h"
@@ -49,6 +50,10 @@ static int guest_x, guest_y;
static SDL_Cursor *guest_sprite;
static Notifier mouse_mode_notifier;
+#define SDL2_REFRESH_INTERVAL_BUSY 10
+#define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \
+ / SDL2_REFRESH_INTERVAL_BUSY + 1)
+
static void sdl_update_caption(struct sdl2_console *scon);
static struct sdl2_console *get_scon_from_window(uint32_t window_id)
@@ -256,7 +261,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy,
int x, int y, int state)
{
- static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
[INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
[INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
@@ -578,6 +583,7 @@ static void handle_windowevent(SDL_Event *ev)
void sdl2_poll_events(struct sdl2_console *scon)
{
SDL_Event ev1, *ev = &ev1;
+ int idle = 1;
if (scon->last_vm_running != runstate_is_running()) {
scon->last_vm_running = runstate_is_running();
@@ -587,12 +593,15 @@ void sdl2_poll_events(struct sdl2_console *scon)
while (SDL_PollEvent(ev)) {
switch (ev->type) {
case SDL_KEYDOWN:
+ idle = 0;
handle_keydown(ev);
break;
case SDL_KEYUP:
+ idle = 0;
handle_keyup(ev);
break;
case SDL_TEXTINPUT:
+ idle = 0;
handle_textinput(ev);
break;
case SDL_QUIT:
@@ -602,13 +611,16 @@ void sdl2_poll_events(struct sdl2_console *scon)
}
break;
case SDL_MOUSEMOTION:
+ idle = 0;
handle_mousemotion(ev);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
+ idle = 0;
handle_mousebutton(ev);
break;
case SDL_MOUSEWHEEL:
+ idle = 0;
handle_mousewheel(ev);
break;
case SDL_WINDOWEVENT:
@@ -618,6 +630,18 @@ void sdl2_poll_events(struct sdl2_console *scon)
break;
}
}
+
+ if (idle) {
+ if (scon->idle_counter < SDL2_MAX_IDLE_COUNT) {
+ scon->idle_counter++;
+ if (scon->idle_counter >= SDL2_MAX_IDLE_COUNT) {
+ scon->dcl.update_interval = GUI_REFRESH_INTERVAL_DEFAULT;
+ }
+ }
+ } else {
+ scon->idle_counter = 0;
+ scon->dcl.update_interval = SDL2_REFRESH_INTERVAL_BUSY;
+ }
}
static void sdl_mouse_warp(DisplayChangeListener *dcl,
@@ -700,6 +724,13 @@ static const DisplayChangeListenerOps dcl_gl_ops = {
.dpy_refresh = sdl2_gl_refresh,
.dpy_mouse_set = sdl_mouse_warp,
.dpy_cursor_define = sdl_mouse_define,
+
+ .dpy_gl_ctx_create = sdl2_gl_create_context,
+ .dpy_gl_ctx_destroy = sdl2_gl_destroy_context,
+ .dpy_gl_ctx_make_current = sdl2_gl_make_context_current,
+ .dpy_gl_ctx_get_current = sdl2_gl_get_current_context,
+ .dpy_gl_scanout = sdl2_gl_scanout,
+ .dpy_gl_update = sdl2_gl_scanout_flush,
};
#endif
diff --git a/qemu/ui/sdl_zoom.c b/qemu/ui/sdl_zoom.c
index 2625c4557..72622c264 100644
--- a/qemu/ui/sdl_zoom.c
+++ b/qemu/ui/sdl_zoom.c
@@ -11,11 +11,9 @@
*
*/
-#include "sdl_zoom.h"
#include "qemu/osdep.h"
+#include "sdl_zoom.h"
#include <glib.h>
-#include <stdint.h>
-#include <stdio.h>
static void sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth,
SDL_Rect *dst_rect);
diff --git a/qemu/ui/shader.c b/qemu/ui/shader.c
index 52a463293..9264009b8 100644
--- a/qemu/ui/shader.c
+++ b/qemu/ui/shader.c
@@ -24,26 +24,48 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/shader.h"
/* ---------------------------------------------------------------------- */
-void qemu_gl_run_texture_blit(GLint texture_blit_prog)
+GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog)
{
- GLfloat in_position[] = {
+ static const GLfloat in_position[] = {
-1, -1,
1, -1,
-1, 1,
1, 1,
};
GLint l_position;
+ GLuint vao, buffer;
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* this is the VBO that holds the vertex data */
+ glGenBuffers(1, &buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(in_position), in_position,
+ GL_STATIC_DRAW);
- glUseProgram(texture_blit_prog);
l_position = glGetAttribLocation(texture_blit_prog, "in_position");
- glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, in_position);
+ glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(l_position);
- glDrawArrays(GL_TRIANGLE_STRIP, l_position, 4);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ return vao;
+}
+
+void qemu_gl_run_texture_blit(GLint texture_blit_prog,
+ GLint texture_blit_vao)
+{
+ glUseProgram(texture_blit_prog);
+ glBindVertexArray(texture_blit_vao);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
/* ---------------------------------------------------------------------- */
diff --git a/qemu/ui/spice-core.c b/qemu/ui/spice-core.c
index bf4fd0749..61db3c18b 100644
--- a/qemu/ui/spice-core.c
+++ b/qemu/ui/spice-core.c
@@ -15,6 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include <spice.h>
#include <netdb.h>
@@ -200,8 +201,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
{
SpiceServerInfo *server = g_malloc0(sizeof(*server));
SpiceChannel *client = g_malloc0(sizeof(*client));
- server->base = g_malloc0(sizeof(*server->base));
- client->base = g_malloc0(sizeof(*client->base));
/*
* Spice server might have called us from spice worker thread
@@ -218,9 +217,11 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
}
if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
- add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext,
+ add_addr_info(qapi_SpiceChannel_base(client),
+ (struct sockaddr *)&info->paddr_ext,
info->plen_ext);
- add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext,
+ add_addr_info(qapi_SpiceServerInfo_base(server),
+ (struct sockaddr *)&info->laddr_ext,
info->llen_ext);
} else {
error_report("spice: %s, extended address is expected",
@@ -229,7 +230,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
switch (event) {
case SPICE_CHANNEL_EVENT_CONNECTED:
- qapi_event_send_spice_connected(server->base, client->base, &error_abort);
+ qapi_event_send_spice_connected(qapi_SpiceServerInfo_base(server),
+ qapi_SpiceChannel_base(client),
+ &error_abort);
break;
case SPICE_CHANNEL_EVENT_INITIALIZED:
if (auth) {
@@ -242,7 +245,9 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
break;
case SPICE_CHANNEL_EVENT_DISCONNECTED:
channel_list_del(info);
- qapi_event_send_spice_disconnected(server->base, client->base, &error_abort);
+ qapi_event_send_spice_disconnected(qapi_SpiceServerInfo_base(server),
+ qapi_SpiceChannel_base(client),
+ &error_abort);
break;
default:
break;
@@ -378,16 +383,15 @@ static SpiceChannelList *qmp_query_spice_channels(void)
chan = g_malloc0(sizeof(*chan));
chan->value = g_malloc0(sizeof(*chan->value));
- chan->value->base = g_malloc0(sizeof(*chan->value->base));
paddr = (struct sockaddr *)&item->info->paddr_ext;
plen = item->info->plen_ext;
getnameinfo(paddr, plen,
host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
- chan->value->base->host = g_strdup(host);
- chan->value->base->port = g_strdup(port);
- chan->value->base->family = inet_netfamily(paddr->sa_family);
+ chan->value->host = g_strdup(host);
+ chan->value->port = g_strdup(port);
+ chan->value->family = inet_netfamily(paddr->sa_family);
chan->value->connection_id = item->info->connection_id;
chan->value->channel_type = item->info->type;
@@ -490,9 +494,14 @@ static QemuOptsList qemu_spice_opts = {
},{
.name = "playback-compression",
.type = QEMU_OPT_BOOL,
- }, {
+ },{
.name = "seamless-migration",
.type = QEMU_OPT_BOOL,
+#ifdef HAVE_SPICE_GL
+ },{
+ .name = "gl",
+ .type = QEMU_OPT_BOOL,
+#endif
},
{ /* end of list */ }
},
@@ -564,7 +573,8 @@ static void migration_state_notifier(Notifier *notifier, void *data)
if (migration_in_setup(s)) {
spice_server_migrate_start(spice_server);
- } else if (migration_has_finished(s)) {
+ } else if (migration_has_finished(s) ||
+ migration_in_postcopy_after_devices(s)) {
spice_server_migrate_end(spice_server, true);
spice_have_target_host = false;
} else if (migration_has_failed(s)) {
@@ -724,8 +734,7 @@ void qemu_spice_init(void)
qemu_spice_set_passwd(password, false, false);
}
if (qemu_opt_get_bool(opts, "sasl", 0)) {
- if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
- spice_server_set_sasl(spice_server, 1) == -1) {
+ if (spice_server_set_sasl(spice_server, 1) == -1) {
error_report("spice: failed to enable sasl");
exit(1);
}
@@ -791,6 +800,7 @@ void qemu_spice_init(void)
seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
spice_server_set_seamless_migration(spice_server, seamless_migration);
+ spice_server_set_sasl_appname(spice_server, "qemu");
if (spice_server_init(spice_server, &core_interface) != 0) {
error_report("failed to initialize spice server");
exit(1);
@@ -815,6 +825,19 @@ void qemu_spice_init(void)
#if SPICE_SERVER_VERSION >= 0x000c02
qemu_spice_register_ports();
#endif
+
+#ifdef HAVE_SPICE_GL
+ if (qemu_opt_get_bool(opts, "gl", 0)) {
+ if ((port != 0) || (tls_port != 0)) {
+ error_report("SPICE GL support is local-only for now and "
+ "incompatible with -spice port/tls-port");
+ exit(1);
+ }
+ if (egl_rendernode_init() == 0) {
+ display_opengl = 1;
+ }
+ }
+#endif
}
int qemu_spice_add_interface(SpiceBaseInstance *sin)
@@ -927,4 +950,4 @@ static void spice_register_config(void)
{
qemu_add_opts(&qemu_spice_opts);
}
-machine_init(spice_register_config);
+opts_init(spice_register_config);
diff --git a/qemu/ui/spice-display.c b/qemu/ui/spice-display.c
index 0360abfd2..242ab5f46 100644
--- a/qemu/ui/spice-display.c
+++ b/qemu/ui/spice-display.c
@@ -15,6 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "ui/qemu-spice.h"
#include "qemu/timer.h"
@@ -408,7 +409,8 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
if (surface && ssd->surface &&
surface_width(surface) == pixman_image_get_width(ssd->surface) &&
- surface_height(surface) == pixman_image_get_height(ssd->surface)) {
+ surface_height(surface) == pixman_image_get_height(ssd->surface) &&
+ surface_format(surface) == pixman_image_get_format(ssd->surface)) {
/* no-resize fast path: just swap backing store */
dprint(1, "%s/%d: fast (%dx%d)\n", __func__, ssd->qxl.id,
surface_width(surface), surface_height(surface));
@@ -458,6 +460,13 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
memset(&ssd->dirty, 0, sizeof(ssd->dirty));
ssd->notify++;
+
+ qemu_mutex_lock(&ssd->lock);
+ if (ssd->cursor) {
+ g_free(ssd->ptr_define);
+ ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0);
+ }
+ qemu_mutex_unlock(&ssd->lock);
}
static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
@@ -465,8 +474,6 @@ static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
if (ssd->cursor) {
assert(ssd->dcl.con);
dpy_cursor_define(ssd->dcl.con, ssd->cursor);
- cursor_put(ssd->cursor);
- ssd->cursor = NULL;
}
if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
assert(ssd->dcl.con);
@@ -561,7 +568,7 @@ static int interface_get_command(QXLInstance *sin, QXLCommandExt *ext)
static int interface_req_cmd_notification(QXLInstance *sin)
{
- dprint(1, "%s/%d:\n", __func__, sin->id);
+ dprint(2, "%s/%d:\n", __func__, sin->id);
return 1;
}
@@ -614,7 +621,7 @@ static int interface_get_cursor_command(QXLInstance *sin, QXLCommandExt *ext)
static int interface_req_cursor_notification(QXLInstance *sin)
{
- dprint(1, "%s:\n", __FUNCTION__);
+ dprint(2, "%s:\n", __func__);
return 1;
}
@@ -643,9 +650,23 @@ static void interface_update_area_complete(QXLInstance *sin,
/* called from spice server thread context only */
static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
{
- /* should never be called, used in qxl native mode only */
- fprintf(stderr, "%s: abort()\n", __func__);
- abort();
+ QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
+
+ switch (cookie->type) {
+#ifdef HAVE_SPICE_GL
+ case QXL_COOKIE_TYPE_GL_DRAW_DONE:
+ {
+ SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
+ qemu_bh_schedule(ssd->gl_unblock_bh);
+ break;
+ }
+#endif
+ default:
+ /* should never be called, used in qxl native mode only */
+ fprintf(stderr, "%s: abort()\n", __func__);
+ abort();
+ }
+ g_free(cookie);
}
static void interface_set_client_capabilities(QXLInstance *sin,
@@ -737,9 +758,7 @@ static void display_mouse_set(DisplayChangeListener *dcl,
qemu_mutex_lock(&ssd->lock);
ssd->ptr_x = x;
ssd->ptr_y = y;
- if (ssd->ptr_move) {
- g_free(ssd->ptr_move);
- }
+ g_free(ssd->ptr_move);
ssd->ptr_move = qemu_spice_create_cursor_update(ssd, NULL, on);
qemu_mutex_unlock(&ssd->lock);
}
@@ -750,15 +769,16 @@ static void display_mouse_define(DisplayChangeListener *dcl,
SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
qemu_mutex_lock(&ssd->lock);
+ if (c) {
+ cursor_get(c);
+ }
+ cursor_put(ssd->cursor);
+ ssd->cursor = c;
ssd->hot_x = c->hot_x;
ssd->hot_y = c->hot_y;
- if (ssd->ptr_move) {
- g_free(ssd->ptr_move);
- ssd->ptr_move = NULL;
- }
- if (ssd->ptr_define) {
- g_free(ssd->ptr_define);
- }
+ g_free(ssd->ptr_move);
+ ssd->ptr_move = NULL;
+ g_free(ssd->ptr_define);
ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0);
qemu_mutex_unlock(&ssd->lock);
}
@@ -773,20 +793,128 @@ static const DisplayChangeListenerOps display_listener_ops = {
.dpy_cursor_define = display_mouse_define,
};
+#ifdef HAVE_SPICE_GL
+
+static void qemu_spice_gl_block(SimpleSpiceDisplay *ssd, bool block)
+{
+ uint64_t timeout;
+
+ if (block) {
+ timeout = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+ timeout += 1000; /* one sec */
+ timer_mod(ssd->gl_unblock_timer, timeout);
+ } else {
+ timer_del(ssd->gl_unblock_timer);
+ }
+ graphic_hw_gl_block(ssd->dcl.con, block);
+}
+
+static void qemu_spice_gl_unblock_bh(void *opaque)
+{
+ SimpleSpiceDisplay *ssd = opaque;
+
+ qemu_spice_gl_block(ssd, false);
+}
+
+static void qemu_spice_gl_block_timer(void *opaque)
+{
+ fprintf(stderr, "WARNING: spice: no gl-draw-done within one second\n");
+}
+
+static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ qemu_egl_rn_ctx);
+ return qemu_egl_create_context(dcl, params);
+}
+
+static void qemu_spice_gl_scanout(DisplayChangeListener *dcl,
+ uint32_t tex_id,
+ bool y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+ EGLint stride = 0, fourcc = 0;
+ int fd = -1;
+
+ if (tex_id) {
+ fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc);
+ if (fd < 0) {
+ fprintf(stderr, "%s: failed to get fd for texture\n", __func__);
+ return;
+ }
+ dprint(1, "%s: %dx%d (stride %d, fourcc 0x%x)\n", __func__,
+ w, h, stride, fourcc);
+ } else {
+ dprint(1, "%s: no texture (no framebuffer)\n", __func__);
+ }
+
+ assert(!tex_id || fd >= 0);
+
+ /* note: spice server will close the fd */
+ spice_qxl_gl_scanout(&ssd->qxl, fd,
+ surface_width(ssd->ds),
+ surface_height(ssd->ds),
+ stride, fourcc, y_0_top);
+}
+
+static void qemu_spice_gl_update(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl);
+ uint64_t cookie;
+
+ dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y);
+ qemu_spice_gl_block(ssd, true);
+ cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0);
+ spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie);
+}
+
+static const DisplayChangeListenerOps display_listener_gl_ops = {
+ .dpy_name = "spice-egl",
+ .dpy_gfx_update = display_update,
+ .dpy_gfx_switch = display_switch,
+ .dpy_gfx_check_format = qemu_pixman_check_format,
+ .dpy_refresh = display_refresh,
+ .dpy_mouse_set = display_mouse_set,
+ .dpy_cursor_define = display_mouse_define,
+
+ .dpy_gl_ctx_create = qemu_spice_gl_create_context,
+ .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
+ .dpy_gl_ctx_make_current = qemu_egl_make_context_current,
+ .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
+
+ .dpy_gl_scanout = qemu_spice_gl_scanout,
+ .dpy_gl_update = qemu_spice_gl_update,
+};
+
+#endif /* HAVE_SPICE_GL */
+
static void qemu_spice_display_init_one(QemuConsole *con)
{
SimpleSpiceDisplay *ssd = g_new0(SimpleSpiceDisplay, 1);
qemu_spice_display_init_common(ssd);
+ ssd->dcl.ops = &display_listener_ops;
+#ifdef HAVE_SPICE_GL
+ if (display_opengl) {
+ ssd->dcl.ops = &display_listener_gl_ops;
+ ssd->dmabuf_fd = -1;
+ ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd);
+ ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ qemu_spice_gl_block_timer, ssd);
+ }
+#endif
+ ssd->dcl.con = con;
+
ssd->qxl.base.sif = &dpy_interface.base;
qemu_spice_add_display_interface(&ssd->qxl, con);
assert(ssd->worker);
-
qemu_spice_create_host_memslot(ssd);
- ssd->dcl.ops = &display_listener_ops;
- ssd->dcl.con = con;
register_displaychangelistener(&ssd->dcl);
}
diff --git a/qemu/ui/spice-input.c b/qemu/ui/spice-input.c
index c342e0dcf..8eeebdbb2 100644
--- a/qemu/ui/spice-input.c
+++ b/qemu/ui/spice-input.c
@@ -15,10 +15,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
+#include "qemu/osdep.h"
#include <spice.h>
#include <spice/enums.h>
@@ -107,7 +104,7 @@ typedef struct QemuSpicePointer {
static void spice_update_buttons(QemuSpicePointer *pointer,
int wheel, uint32_t button_mask)
{
- static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_MIDDLE] = 0x04,
[INPUT_BUTTON_RIGHT] = 0x02,
diff --git a/qemu/ui/vnc-auth-sasl.c b/qemu/ui/vnc-auth-sasl.c
index 62a5fc4bf..5ae29c14c 100644
--- a/qemu/ui/vnc-auth-sasl.c
+++ b/qemu/ui/vnc-auth-sasl.c
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "vnc.h"
/* Max amount of data we send/recv for SASL steps to prevent DOS */
@@ -62,7 +64,7 @@ long vnc_client_write_sasl(VncState *vs)
(const char **)&vs->sasl.encoded,
&vs->sasl.encodedLength);
if (err != SASL_OK)
- return vnc_client_io_error(vs, -1, EIO);
+ return vnc_client_io_error(vs, -1, NULL);
vs->sasl.encodedOffset = 0;
}
@@ -86,7 +88,11 @@ long vnc_client_write_sasl(VncState *vs)
* SASL encoded output
*/
if (vs->output.offset == 0) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
return ret;
@@ -110,7 +116,7 @@ long vnc_client_read_sasl(VncState *vs)
&decoded, &decodedLen);
if (err != SASL_OK)
- return vnc_client_io_error(vs, -1, -EIO);
+ return vnc_client_io_error(vs, -1, NULL);
VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
encoded, ret, decoded, decodedLen);
buffer_reserve(&vs->input, decodedLen);
@@ -255,17 +261,17 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
} else {
if (!vnc_auth_sasl_check_ssf(vs)) {
- VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+ VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
goto authreject;
}
/* Check username whitelist ACL */
if (vnc_auth_sasl_check_access(vs) < 0) {
- VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
goto authreject;
}
- VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ VNC_DEBUG("Authentication successful %p\n", vs->ioc);
vnc_write_u32(vs, 0); /* Accept auth */
/*
* Delay writing in SSF encoded mode until pending output
@@ -383,17 +389,17 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l
vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4);
} else {
if (!vnc_auth_sasl_check_ssf(vs)) {
- VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock);
+ VNC_DEBUG("Authentication rejected for weak SSF %p\n", vs->ioc);
goto authreject;
}
/* Check username whitelist ACL */
if (vnc_auth_sasl_check_access(vs) < 0) {
- VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ VNC_DEBUG("Authentication rejected for ACL %p\n", vs->ioc);
goto authreject;
}
- VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ VNC_DEBUG("Authentication successful %p\n", vs->ioc);
vnc_write_u32(vs, 0); /* Accept auth */
start_client_init(vs);
}
@@ -487,6 +493,33 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s
return 0;
}
+static char *
+vnc_socket_ip_addr_string(QIOChannelSocket *ioc,
+ bool local,
+ Error **errp)
+{
+ SocketAddress *addr;
+ char *ret;
+
+ if (local) {
+ addr = qio_channel_socket_get_local_address(ioc, errp);
+ } else {
+ addr = qio_channel_socket_get_remote_address(ioc, errp);
+ }
+ if (!addr) {
+ return NULL;
+ }
+
+ if (addr->type != SOCKET_ADDRESS_KIND_INET) {
+ error_setg(errp, "Not an inet socket type");
+ return NULL;
+ }
+ ret = g_strdup_printf("%s;%s", addr->u.inet.data->host,
+ addr->u.inet.data->port);
+ qapi_free_SocketAddress(addr);
+ return ret;
+}
+
void start_auth_sasl(VncState *vs)
{
const char *mechlist = NULL;
@@ -495,13 +528,16 @@ void start_auth_sasl(VncState *vs)
char *localAddr, *remoteAddr;
int mechlistlen;
- VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
+ VNC_DEBUG("Initialize SASL auth %p\n", vs->ioc);
/* Get local & remote client addresses in form IPADDR;PORT */
- if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
+ localAddr = vnc_socket_ip_addr_string(vs->sioc, true, NULL);
+ if (!localAddr) {
goto authabort;
+ }
- if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
+ remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, NULL);
+ if (!remoteAddr) {
g_free(localAddr);
goto authabort;
}
@@ -525,21 +561,24 @@ void start_auth_sasl(VncState *vs)
goto authabort;
}
-#ifdef CONFIG_VNC_TLS
/* Inform SASL that we've got an external SSF layer from TLS/x509 */
if (vs->auth == VNC_AUTH_VENCRYPT &&
vs->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
- gnutls_cipher_algorithm_t cipher;
+ Error *local_err = NULL;
+ int keysize;
sasl_ssf_t ssf;
- cipher = gnutls_cipher_get(vs->tls.session);
- if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) {
- VNC_DEBUG("%s", "cannot TLS get cipher size\n");
+ keysize = qcrypto_tls_session_get_key_size(vs->tls,
+ &local_err);
+ if (keysize < 0) {
+ VNC_DEBUG("cannot TLS get cipher size: %s\n",
+ error_get_pretty(local_err));
+ error_free(local_err);
sasl_dispose(&vs->sasl.conn);
vs->sasl.conn = NULL;
goto authabort;
}
- ssf *= 8; /* tls key size is bytes, sasl wants bits */
+ ssf = keysize * CHAR_BIT; /* tls key size is bytes, sasl wants bits */
err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
if (err != SASL_OK) {
@@ -549,20 +588,19 @@ void start_auth_sasl(VncState *vs)
vs->sasl.conn = NULL;
goto authabort;
}
- } else
-#endif /* CONFIG_VNC_TLS */
+ } else {
vs->sasl.wantSSF = 1;
+ }
memset (&secprops, 0, sizeof secprops);
- /* Inform SASL that we've got an external SSF layer from TLS */
- if (vs->vd->is_unix
-#ifdef CONFIG_VNC_TLS
- /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
- is not sufficiently strong */
- || (vs->auth == VNC_AUTH_VENCRYPT &&
- vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-#endif /* CONFIG_VNC_TLS */
- ) {
+ /* Inform SASL that we've got an external SSF layer from TLS.
+ *
+ * Disable SSF, if using TLS+x509+SASL only. TLS without x509
+ * is not sufficiently strong
+ */
+ if (vs->vd->is_unix ||
+ (vs->auth == VNC_AUTH_VENCRYPT &&
+ vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) {
/* If we've got TLS or UNIX domain sock, we don't care about SSF */
secprops.min_ssf = 0;
secprops.max_ssf = 0;
diff --git a/qemu/ui/vnc-auth-vencrypt.c b/qemu/ui/vnc-auth-vencrypt.c
index 8fc965b4a..11c8c9a81 100644
--- a/qemu/ui/vnc-auth-vencrypt.c
+++ b/qemu/ui/vnc-auth-vencrypt.c
@@ -24,7 +24,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc.h"
+#include "qapi/error.h"
#include "qemu/main-loop.h"
static void start_auth_vencrypt_subauth(VncState *vs)
@@ -63,60 +65,23 @@ static void start_auth_vencrypt_subauth(VncState *vs)
}
}
-static void vnc_tls_handshake_io(void *opaque);
-
-static int vnc_start_vencrypt_handshake(VncState *vs)
+static void vnc_tls_handshake_done(Object *source,
+ Error *err,
+ gpointer user_data)
{
- int ret;
-
- if ((ret = gnutls_handshake(vs->tls.session)) < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session))
- qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs);
- else
- qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs);
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
- vnc_client_error(vs);
- return -1;
- }
+ VncState *vs = user_data;
- if (vs->vd->tls.x509verify) {
- if (vnc_tls_validate_certificate(vs) < 0) {
- VNC_DEBUG("Client verification failed\n");
- vnc_client_error(vs);
- return -1;
- } else {
- VNC_DEBUG("Client verification passed\n");
- }
+ if (err) {
+ VNC_DEBUG("Handshake failed %s\n",
+ error_get_pretty(err));
+ vnc_client_error(vs);
+ } else {
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
+ start_auth_vencrypt_subauth(vs);
}
-
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
-
- start_auth_vencrypt_subauth(vs);
-
- return 0;
}
-static void vnc_tls_handshake_io(void *opaque)
-{
- VncState *vs = (VncState *)opaque;
-
- VNC_DEBUG("Handshake IO continue\n");
- vnc_start_vencrypt_handshake(vs);
-}
-
-
-
-#define NEED_X509_AUTH(vs) \
- ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
- (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL)
-
static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
{
@@ -128,20 +93,38 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
vnc_flush(vs);
vnc_client_error(vs);
} else {
+ Error *err = NULL;
+ QIOChannelTLS *tls;
VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth);
vnc_write_u8(vs, 1); /* Accept auth */
vnc_flush(vs);
- if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) {
- VNC_DEBUG("Failed to setup TLS\n");
- return 0;
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ vs->ioc_tag = 0;
}
- VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
- if (vnc_start_vencrypt_handshake(vs) < 0) {
- VNC_DEBUG("Failed to start TLS handshake\n");
+ tls = qio_channel_tls_new_server(
+ vs->ioc,
+ vs->vd->tlscreds,
+ vs->vd->tlsaclname,
+ &err);
+ if (!tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
+ error_free(err);
+ vnc_client_error(vs);
return 0;
}
+
+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
+ object_unref(OBJECT(vs->ioc));
+ vs->ioc = QIO_CHANNEL(tls);
+ vs->tls = qio_channel_tls_get_session(tls);
+
+ qio_channel_tls_handshake(tls,
+ vnc_tls_handshake_done,
+ vs,
+ NULL);
}
return 0;
}
diff --git a/qemu/ui/vnc-enc-hextile.c b/qemu/ui/vnc-enc-hextile.c
index 2e768fd89..4215bd7da 100644
--- a/qemu/ui/vnc-enc-hextile.c
+++ b/qemu/ui/vnc-enc-hextile.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc.h"
static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
diff --git a/qemu/ui/vnc-enc-tight.c b/qemu/ui/vnc-enc-tight.c
index 9a9ddf2e3..e5cba0e5a 100644
--- a/qemu/ui/vnc-enc-tight.c
+++ b/qemu/ui/vnc-enc-tight.c
@@ -26,7 +26,7 @@
* THE SOFTWARE.
*/
-#include "config-host.h"
+#include "qemu/osdep.h"
/* This needs to be before jpeglib.h line because of conflict with
INT32 definitions between jmorecfg.h (included by jpeglib.h) and
@@ -40,7 +40,6 @@
#include <png.h>
#endif
#ifdef CONFIG_VNC_JPEG
-#include <stdio.h>
#include <jpeglib.h>
#endif
diff --git a/qemu/ui/vnc-enc-zlib.c b/qemu/ui/vnc-enc-zlib.c
index d1b97f251..33e9df2f6 100644
--- a/qemu/ui/vnc-enc-zlib.c
+++ b/qemu/ui/vnc-enc-zlib.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc.h"
#define ZALLOC_ALIGNMENT 16
diff --git a/qemu/ui/vnc-enc-zrle-template.c b/qemu/ui/vnc-enc-zrle-template.c
index 70ae624ee..abf6b86e4 100644
--- a/qemu/ui/vnc-enc-zrle-template.c
+++ b/qemu/ui/vnc-enc-zrle-template.c
@@ -22,7 +22,7 @@
*/
-#include <assert.h>
+#include "qemu/osdep.h"
#undef ZRLE_ENDIAN_SUFFIX
diff --git a/qemu/ui/vnc-enc-zrle.c b/qemu/ui/vnc-enc-zrle.c
index ed3b48465..5489870e7 100644
--- a/qemu/ui/vnc-enc-zrle.c
+++ b/qemu/ui/vnc-enc-zrle.c
@@ -26,6 +26,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc.h"
#include "vnc-enc-zrle.h"
diff --git a/qemu/ui/vnc-enc-zywrle-template.c b/qemu/ui/vnc-enc-zywrle-template.c
index 561f7bfab..b446380a7 100644
--- a/qemu/ui/vnc-enc-zywrle-template.c
+++ b/qemu/ui/vnc-enc-zywrle-template.c
@@ -100,6 +100,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endif
#define ZYWRLE_QUANTIZE
+#include "qemu/osdep.h"
#include "vnc-enc-zywrle.h"
#ifndef ZRLE_COMPACT_PIXEL
diff --git a/qemu/ui/vnc-jobs.c b/qemu/ui/vnc-jobs.c
index 22c9abce5..98ca978b0 100644
--- a/qemu/ui/vnc-jobs.c
+++ b/qemu/ui/vnc-jobs.c
@@ -26,9 +26,11 @@
*/
+#include "qemu/osdep.h"
#include "vnc.h"
#include "vnc-jobs.h"
#include "qemu/sockets.h"
+#include "qemu/main-loop.h"
#include "block/aio.h"
/*
@@ -54,7 +56,6 @@ struct VncJobQueue {
QemuCond cond;
QemuMutex mutex;
QemuThread thread;
- Buffer buffer;
bool exit;
QTAILQ_HEAD(, VncJob) jobs;
};
@@ -79,7 +80,7 @@ static void vnc_unlock_queue(VncJobQueue *queue)
VncJob *vnc_job_new(VncState *vs)
{
- VncJob *job = g_malloc0(sizeof(VncJob));
+ VncJob *job = g_new0(VncJob, 1);
job->vs = vs;
vnc_lock_queue(queue);
@@ -90,7 +91,7 @@ VncJob *vnc_job_new(VncState *vs)
int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
{
- VncRectEntry *entry = g_malloc0(sizeof(VncRectEntry));
+ VncRectEntry *entry = g_new0(VncRectEntry, 1);
entry->rect.x = x;
entry->rect.y = y;
@@ -166,10 +167,16 @@ void vnc_jobs_consume_buffer(VncState *vs)
vnc_lock_output(vs);
if (vs->jobs_buffer.offset) {
- vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);
- buffer_reset(&vs->jobs_buffer);
+ if (vs->ioc != NULL && buffer_empty(&vs->output)) {
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
+ }
+ buffer_move(&vs->output, &vs->jobs_buffer);
}
- flush = vs->csock != -1 && vs->abort != true;
+ flush = vs->ioc != NULL && vs->abort != true;
vnc_unlock_output(vs);
if (flush) {
@@ -182,6 +189,10 @@ void vnc_jobs_consume_buffer(VncState *vs)
*/
static void vnc_async_encoding_start(VncState *orig, VncState *local)
{
+ buffer_init(&local->output, "vnc-worker-output");
+ local->sioc = NULL; /* Don't do any network work on this thread */
+ local->ioc = NULL; /* Don't do any network work on this thread */
+
local->vnc_encoding = orig->vnc_encoding;
local->features = orig->features;
local->vd = orig->vd;
@@ -193,10 +204,6 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local)
local->zlib = orig->zlib;
local->hextile = orig->hextile;
local->zrle = orig->zrle;
- local->output = queue->buffer;
- local->csock = -1; /* Don't do any network work on this thread */
-
- buffer_reset(&local->output);
}
static void vnc_async_encoding_end(VncState *orig, VncState *local)
@@ -206,15 +213,13 @@ static void vnc_async_encoding_end(VncState *orig, VncState *local)
orig->hextile = local->hextile;
orig->zrle = local->zrle;
orig->lossy_rect = local->lossy_rect;
-
- queue->buffer = local->output;
}
static int vnc_worker_thread_loop(VncJobQueue *queue)
{
VncJob *job;
VncRectEntry *entry, *tmp;
- VncState vs;
+ VncState vs = {};
int n_rectangles;
int saved_offset;
@@ -231,10 +236,18 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
}
vnc_lock_output(job->vs);
- if (job->vs->csock == -1 || job->vs->abort == true) {
+ if (job->vs->ioc == NULL || job->vs->abort == true) {
vnc_unlock_output(job->vs);
goto disconnected;
}
+ if (buffer_empty(&job->vs->output)) {
+ /*
+ * Looks like a NOP as it obviously moves no data. But it
+ * moves the empty buffer, so we don't have to malloc a new
+ * one for vs.output
+ */
+ buffer_move_empty(&vs.output, &job->vs->output);
+ }
vnc_unlock_output(job->vs);
/* Make a local copy of vs and switch output buffers */
@@ -251,7 +264,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
int n;
- if (job->vs->csock == -1) {
+ if (job->vs->ioc == NULL) {
vnc_unlock_display(job->vs->vd);
/* Copy persistent encoding data */
vnc_async_encoding_end(job->vs, &vs);
@@ -273,15 +286,14 @@ static int vnc_worker_thread_loop(VncJobQueue *queue)
vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
vnc_lock_output(job->vs);
- if (job->vs->csock != -1) {
- buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
- buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
- vs.output.offset);
+ if (job->vs->ioc != NULL) {
+ buffer_move(&job->vs->jobs_buffer, &vs.output);
/* Copy persistent encoding data */
vnc_async_encoding_end(job->vs, &vs);
qemu_bh_schedule(job->vs->bh);
} else {
+ buffer_reset(&vs.output);
/* Copy persistent encoding data */
vnc_async_encoding_end(job->vs, &vs);
}
@@ -298,7 +310,7 @@ disconnected:
static VncJobQueue *vnc_queue_init(void)
{
- VncJobQueue *queue = g_malloc0(sizeof(VncJobQueue));
+ VncJobQueue *queue = g_new0(VncJobQueue, 1);
qemu_cond_init(&queue->cond);
qemu_mutex_init(&queue->mutex);
@@ -310,7 +322,6 @@ static void vnc_queue_clear(VncJobQueue *q)
{
qemu_cond_destroy(&queue->cond);
qemu_mutex_destroy(&queue->mutex);
- buffer_free(&queue->buffer);
g_free(q);
queue = NULL; /* Unset global queue */
}
diff --git a/qemu/ui/vnc-palette.c b/qemu/ui/vnc-palette.c
index c130deee9..3b89d1af2 100644
--- a/qemu/ui/vnc-palette.c
+++ b/qemu/ui/vnc-palette.c
@@ -26,9 +26,9 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc-palette.h"
#include <glib.h>
-#include <string.h>
static VncPaletteEntry *palette_find(const VncPalette *palette,
uint32_t color, unsigned int hash)
diff --git a/qemu/ui/vnc-palette.h b/qemu/ui/vnc-palette.h
index d02f0236c..1bd4318f5 100644
--- a/qemu/ui/vnc-palette.h
+++ b/qemu/ui/vnc-palette.h
@@ -31,8 +31,6 @@
#include "qapi/qmp/qlist.h"
#include "qemu/queue.h"
-#include <stdint.h>
-#include <stdbool.h>
#define VNC_PALETTE_HASH_SIZE 256
#define VNC_PALETTE_MAX_SIZE 256
diff --git a/qemu/ui/vnc-tls.c b/qemu/ui/vnc-tls.c
deleted file mode 100644
index 028fc4db1..000000000
--- a/qemu/ui/vnc-tls.c
+++ /dev/null
@@ -1,474 +0,0 @@
-/*
- * QEMU VNC display driver: TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu-x509.h"
-#include "vnc.h"
-#include "qemu/sockets.h"
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
-/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
-static void vnc_debug_gnutls_log(int level, const char* str) {
- VNC_DEBUG("%d %s", level, str);
-}
-#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */
-
-
-#define DH_BITS 1024
-static gnutls_dh_params_t dh_params;
-
-static int vnc_tls_initialize(void)
-{
- static int tlsinitialized = 0;
-
- if (tlsinitialized)
- return 1;
-
- if (gnutls_global_init () < 0)
- return 0;
-
- /* XXX ought to re-generate diffie-hellman params periodically */
- if (gnutls_dh_params_init (&dh_params) < 0)
- return 0;
- if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
- return 0;
-
-#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2
- gnutls_global_set_log_level(10);
- gnutls_global_set_log_function(vnc_debug_gnutls_log);
-#endif
-
- tlsinitialized = 1;
-
- return 1;
-}
-
-static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
- const void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = send(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
- void *data,
- size_t len) {
- VncState *vs = (VncState *)transport;
- int ret;
-
- retry:
- ret = qemu_recv(vs->csock, data, len, 0);
- if (ret < 0) {
- if (errno == EINTR)
- goto retry;
- return -1;
- }
- return ret;
-}
-
-
-static gnutls_anon_server_credentials_t vnc_tls_initialize_anon_cred(void)
-{
- gnutls_anon_server_credentials_t anon_cred;
- int ret;
-
- if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
-
- gnutls_anon_set_server_dh_params(anon_cred, dh_params);
-
- return anon_cred;
-}
-
-
-static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd)
-{
- gnutls_certificate_credentials_t x509_cred;
- int ret;
-
- if (!vd->tls.x509cacert) {
- VNC_DEBUG("No CA x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509cert) {
- VNC_DEBUG("No server x509 certificate specified\n");
- return NULL;
- }
- if (!vd->tls.x509key) {
- VNC_DEBUG("No server private key specified\n");
- return NULL;
- }
-
- if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
- VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
- return NULL;
- }
- if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
- vd->tls.x509cacert,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
- vd->tls.x509cert,
- vd->tls.x509key,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
-
- if (vd->tls.x509cacrl) {
- if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
- vd->tls.x509cacrl,
- GNUTLS_X509_FMT_PEM)) < 0) {
- VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
- gnutls_certificate_free_credentials(x509_cred);
- return NULL;
- }
- }
-
- gnutls_certificate_set_dh_params (x509_cred, dh_params);
-
- return x509_cred;
-}
-
-
-int vnc_tls_validate_certificate(VncState *vs)
-{
- int ret;
- unsigned int status;
- const gnutls_datum_t *certs;
- unsigned int nCerts, i;
- time_t now;
-
- VNC_DEBUG("Validating client certificate\n");
- if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) {
- VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
- return -1;
- }
-
- if ((now = time(NULL)) == ((time_t)-1)) {
- return -1;
- }
-
- if (status != 0) {
- if (status & GNUTLS_CERT_INVALID)
- VNC_DEBUG("The certificate is not trusted.\n");
-
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- VNC_DEBUG("The certificate hasn't got a known issuer.\n");
-
- if (status & GNUTLS_CERT_REVOKED)
- VNC_DEBUG("The certificate has been revoked.\n");
-
- if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
- VNC_DEBUG("The certificate uses an insecure algorithm\n");
-
- return -1;
- } else {
- VNC_DEBUG("Certificate is valid!\n");
- }
-
- /* Only support x509 for now */
- if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509)
- return -1;
-
- if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts)))
- return -1;
-
- for (i = 0 ; i < nCerts ; i++) {
- gnutls_x509_crt_t cert;
- VNC_DEBUG ("Checking certificate chain %d\n", i);
- if (gnutls_x509_crt_init (&cert) < 0)
- return -1;
-
- if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_expiration_time (cert) < now) {
- VNC_DEBUG("The certificate has expired\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (gnutls_x509_crt_get_activation_time (cert) > now) {
- VNC_DEBUG("The certificate is not yet activated\n");
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
-
- if (i == 0) {
- size_t dnameSize = 1024;
- vs->tls.dname = g_malloc(dnameSize);
- requery:
- if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) {
- if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
- vs->tls.dname = g_realloc(vs->tls.dname, dnameSize);
- goto requery;
- }
- gnutls_x509_crt_deinit (cert);
- VNC_DEBUG("Cannot get client distinguished name: %s",
- gnutls_strerror (ret));
- return -1;
- }
-
- if (vs->vd->tls.x509verify) {
- int allow;
- if (!vs->vd->tls.acl) {
- VNC_DEBUG("no ACL activated, allowing access");
- gnutls_x509_crt_deinit (cert);
- continue;
- }
-
- allow = qemu_acl_party_is_allowed(vs->vd->tls.acl,
- vs->tls.dname);
-
- VNC_DEBUG("TLS x509 ACL check for %s is %s\n",
- vs->tls.dname, allow ? "allowed" : "denied");
- if (!allow) {
- gnutls_x509_crt_deinit (cert);
- return -1;
- }
- }
- }
-
- gnutls_x509_crt_deinit (cert);
- }
-
- return 0;
-}
-
-#if defined(GNUTLS_VERSION_NUMBER) && \
- GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH";
- int rc;
-
- rc = gnutls_priority_set_direct(s, priority, NULL);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#else
-
-static int vnc_set_gnutls_priority(gnutls_session_t s, int x509)
-{
- static const int cert_types[] = { GNUTLS_CRT_X509, 0 };
- static const int protocols[] = {
- GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
- };
- static const int kx_anon[] = { GNUTLS_KX_ANON_DH, 0 };
- static const int kx_x509[] = {
- GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA,
- GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0
- };
- int rc;
-
- rc = gnutls_kx_set_priority(s, x509 ? kx_x509 : kx_anon);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_certificate_type_set_priority(s, cert_types);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
-
- rc = gnutls_protocol_set_priority(s, protocols);
- if (rc != GNUTLS_E_SUCCESS) {
- return -1;
- }
- return 0;
-}
-
-#endif
-
-int vnc_tls_client_setup(VncState *vs,
- int needX509Creds) {
- VNC_DEBUG("Do TLS setup\n");
- if (vnc_tls_initialize() < 0) {
- VNC_DEBUG("Failed to init TLS\n");
- vnc_client_error(vs);
- return -1;
- }
- if (vs->tls.session == NULL) {
- if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) {
- vnc_client_error(vs);
- return -1;
- }
-
- if (gnutls_set_default_priority(vs->tls.session) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (vnc_set_gnutls_priority(vs->tls.session, needX509Creds) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
-
- if (needX509Creds) {
- gnutls_certificate_server_credentials x509_cred =
- vnc_tls_initialize_x509_cred(vs->vd);
- if (!x509_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_certificate_free_credentials(x509_cred);
- vnc_client_error(vs);
- return -1;
- }
- if (vs->vd->tls.x509verify) {
- VNC_DEBUG("Requesting a client certificate\n");
- gnutls_certificate_server_set_request(vs->tls.session,
- GNUTLS_CERT_REQUEST);
- }
-
- } else {
- gnutls_anon_server_credentials_t anon_cred =
- vnc_tls_initialize_anon_cred();
- if (!anon_cred) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- vnc_client_error(vs);
- return -1;
- }
- if (gnutls_credentials_set(vs->tls.session,
- GNUTLS_CRD_ANON, anon_cred) < 0) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- gnutls_anon_free_server_credentials(anon_cred);
- vnc_client_error(vs);
- return -1;
- }
- }
-
- gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs);
- gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push);
- gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull);
- }
- return 0;
-}
-
-
-void vnc_tls_client_cleanup(VncState *vs)
-{
- if (vs->tls.session) {
- gnutls_deinit(vs->tls.session);
- vs->tls.session = NULL;
- }
- g_free(vs->tls.dname);
-}
-
-
-
-static int vnc_set_x509_credential(VncDisplay *vd,
- const char *certdir,
- const char *filename,
- char **cred,
- int ignoreMissing)
-{
- struct stat sb;
-
- g_free(*cred);
- *cred = g_malloc(strlen(certdir) + strlen(filename) + 2);
-
- strcpy(*cred, certdir);
- strcat(*cred, "/");
- strcat(*cred, filename);
-
- VNC_DEBUG("Check %s\n", *cred);
- if (stat(*cred, &sb) < 0) {
- g_free(*cred);
- *cred = NULL;
- if (ignoreMissing && errno == ENOENT)
- return 0;
- return -1;
- }
-
- return 0;
-}
-
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *certdir)
-{
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0)
- goto cleanup;
- if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0)
- goto cleanup;
-
- return 0;
-
- cleanup:
- g_free(vd->tls.x509cacert);
- g_free(vd->tls.x509cacrl);
- g_free(vd->tls.x509cert);
- g_free(vd->tls.x509key);
- vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
- return -1;
-}
-
diff --git a/qemu/ui/vnc-tls.h b/qemu/ui/vnc-tls.h
deleted file mode 100644
index f9829c782..000000000
--- a/qemu/ui/vnc-tls.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * QEMU VNC display driver. TLS helpers
- *
- * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
- * Copyright (C) 2006 Fabrice Bellard
- * Copyright (C) 2009 Red Hat, Inc
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-#ifndef __QEMU_VNC_TLS_H__
-#define __QEMU_VNC_TLS_H__
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-
-#include "qemu/acl.h"
-
-typedef struct VncDisplayTLS VncDisplayTLS;
-typedef struct VncStateTLS VncStateTLS;
-
-/* Server state */
-struct VncDisplayTLS {
- int x509verify; /* Non-zero if server requests & validates client cert */
- qemu_acl *acl;
-
- /* Paths to x509 certs/keys */
- char *x509cacert;
- char *x509cacrl;
- char *x509cert;
- char *x509key;
-};
-
-/* Per client state */
-struct VncStateTLS {
- gnutls_session_t session;
-
- /* Client's Distinguished Name from the x509 cert */
- char *dname;
-};
-
-int vnc_tls_client_setup(VncState *vs, int x509Creds);
-void vnc_tls_client_cleanup(VncState *vs);
-
-int vnc_tls_validate_certificate(VncState *vs);
-
-int vnc_tls_set_x509_creds_dir(VncDisplay *vd,
- const char *path);
-
-
-#endif /* __QEMU_VNC_TLS_H__ */
-
diff --git a/qemu/ui/vnc-ws.c b/qemu/ui/vnc-ws.c
index b4cb6bde7..7c79a4c37 100644
--- a/qemu/ui/vnc-ws.c
+++ b/qemu/ui/vnc-ws.c
@@ -18,364 +18,108 @@
* along with this software; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "vnc.h"
-#include "qemu/main-loop.h"
-#include "crypto/hash.h"
+#include "io/channel-websock.h"
-#ifdef CONFIG_VNC_TLS
-#include "qemu/sockets.h"
-
-static int vncws_start_tls_handshake(VncState *vs)
+static void vncws_tls_handshake_done(Object *source,
+ Error *err,
+ gpointer user_data)
{
- int ret = gnutls_handshake(vs->tls.session);
+ VncState *vs = user_data;
- if (ret < 0) {
- if (!gnutls_error_is_fatal(ret)) {
- VNC_DEBUG("Handshake interrupted (blocking)\n");
- if (!gnutls_record_get_direction(vs->tls.session)) {
- qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io,
- NULL, vs);
- } else {
- qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
- vs);
- }
- return 0;
- }
- VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
+ if (err) {
+ VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err));
vnc_client_error(vs);
- return -1;
- }
-
- if (vs->vd->tls.x509verify) {
- if (vnc_tls_validate_certificate(vs) < 0) {
- VNC_DEBUG("Client verification failed\n");
- vnc_client_error(vs);
- return -1;
- } else {
- VNC_DEBUG("Client verification passed\n");
- }
+ } else {
+ VNC_DEBUG("TLS handshake complete, starting websocket handshake\n");
+ vs->ioc_tag = qio_channel_add_watch(
+ QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL);
}
-
- VNC_DEBUG("Handshake done, switching to TLS data mode\n");
- qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
-
- return 0;
}
-void vncws_tls_handshake_io(void *opaque)
-{
- VncState *vs = (VncState *)opaque;
- if (!vs->tls.session) {
- VNC_DEBUG("TLS Websocket setup\n");
- if (vnc_tls_client_setup(vs, vs->vd->tls.x509cert != NULL) < 0) {
- return;
- }
- }
- VNC_DEBUG("Handshake IO continue\n");
- vncws_start_tls_handshake(vs);
-}
-#endif /* CONFIG_VNC_TLS */
-
-void vncws_handshake_read(void *opaque)
+gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+ GIOCondition condition G_GNUC_UNUSED,
+ void *opaque)
{
VncState *vs = opaque;
- uint8_t *handshake_end;
- long ret;
- /* Typical HTTP headers from novnc are 512 bytes, so limiting
- * total header size to 4096 is easily enough. */
- size_t want = 4096 - vs->ws_input.offset;
- buffer_reserve(&vs->ws_input, want);
- ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), want);
+ QIOChannelTLS *tls;
+ Error *err = NULL;
- if (!ret) {
- if (vs->csock == -1) {
- vnc_disconnect_finish(vs);
- }
- return;
+ VNC_DEBUG("TLS Websocket connection required\n");
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ vs->ioc_tag = 0;
}
- vs->ws_input.offset += ret;
- handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
- vs->ws_input.offset, WS_HANDSHAKE_END);
- if (handshake_end) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
- vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset);
- buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer +
- strlen(WS_HANDSHAKE_END));
- } else if (vs->ws_input.offset >= 4096) {
- VNC_DEBUG("End of headers not found in first 4096 bytes\n");
+ tls = qio_channel_tls_new_server(
+ vs->ioc,
+ vs->vd->tlscreds,
+ vs->vd->tlsaclname,
+ &err);
+ if (!tls) {
+ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err));
+ error_free(err);
vnc_client_error(vs);
+ return TRUE;
}
-}
-
-
-long vnc_client_read_ws(VncState *vs)
-{
- int ret, err;
- uint8_t *payload;
- size_t payload_size, header_size;
- VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
- vs->ws_input.capacity, vs->ws_input.offset);
- buffer_reserve(&vs->ws_input, 4096);
- ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096);
- if (!ret) {
- return 0;
- }
- vs->ws_input.offset += ret;
- ret = 0;
- /* consume as much of ws_input buffer as possible */
- do {
- if (vs->ws_payload_remain == 0) {
- err = vncws_decode_frame_header(&vs->ws_input,
- &header_size,
- &vs->ws_payload_remain,
- &vs->ws_payload_mask);
- if (err <= 0) {
- return err;
- }
+ VNC_DEBUG("Start TLS WS handshake process\n");
+ object_unref(OBJECT(vs->ioc));
+ vs->ioc = QIO_CHANNEL(tls);
+ vs->tls = qio_channel_tls_get_session(tls);
- buffer_advance(&vs->ws_input, header_size);
- }
- if (vs->ws_payload_remain != 0) {
- err = vncws_decode_frame_payload(&vs->ws_input,
- &vs->ws_payload_remain,
- &vs->ws_payload_mask,
- &payload,
- &payload_size);
- if (err < 0) {
- return err;
- }
- if (err == 0) {
- return ret;
- }
- ret += err;
+ qio_channel_tls_handshake(tls,
+ vncws_tls_handshake_done,
+ vs,
+ NULL);
- buffer_reserve(&vs->input, payload_size);
- buffer_append(&vs->input, payload, payload_size);
-
- buffer_advance(&vs->ws_input, payload_size);
- }
- } while (vs->ws_input.offset > 0);
-
- return ret;
+ return TRUE;
}
-long vnc_client_write_ws(VncState *vs)
-{
- long ret;
- VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
- vs->output.buffer, vs->output.capacity, vs->output.offset);
- vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset);
- buffer_reset(&vs->output);
- ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset);
- if (!ret) {
- return 0;
- }
-
- buffer_advance(&vs->ws_output, ret);
-
- if (vs->ws_output.offset == 0) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
- }
-
- return ret;
-}
-static char *vncws_extract_handshake_entry(const char *handshake,
- size_t handshake_len, const char *name)
+static void vncws_handshake_done(Object *source,
+ Error *err,
+ gpointer user_data)
{
- char *begin, *end, *ret = NULL;
- char *line = g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM, name);
- begin = g_strstr_len(handshake, handshake_len, line);
- if (begin != NULL) {
- begin += strlen(line);
- end = g_strstr_len(begin, handshake_len - (begin - handshake),
- WS_HANDSHAKE_DELIM);
- if (end != NULL) {
- ret = g_strndup(begin, end - begin);
- }
- }
- g_free(line);
- return ret;
-}
-
-static void vncws_send_handshake_response(VncState *vs, const char* key)
-{
- char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1];
- char *accept = NULL, *response = NULL;
- Error *err = NULL;
-
- g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
- g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
+ VncState *vs = user_data;
- /* hash and encode it */
- if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
- combined_key,
- WS_CLIENT_KEY_LEN + WS_GUID_LEN,
- &accept,
- &err) < 0) {
- VNC_DEBUG("Hashing Websocket combined key failed %s\n",
- error_get_pretty(err));
- error_free(err);
+ if (err) {
+ VNC_DEBUG("Websock handshake failed %s\n", error_get_pretty(err));
vnc_client_error(vs);
- return;
- }
-
- response = g_strdup_printf(WS_HANDSHAKE, accept);
- vnc_client_write_buf(vs, (const uint8_t *)response, strlen(response));
-
- g_free(accept);
- g_free(response);
-
- vs->encode_ws = 1;
- vnc_init_state(vs);
-}
-
-void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
-{
- char *protocols = vncws_extract_handshake_entry((const char *)line, size,
- "Sec-WebSocket-Protocol");
- char *version = vncws_extract_handshake_entry((const char *)line, size,
- "Sec-WebSocket-Version");
- char *key = vncws_extract_handshake_entry((const char *)line, size,
- "Sec-WebSocket-Key");
-
- if (protocols && version && key
- && g_strrstr(protocols, "binary")
- && !strcmp(version, WS_SUPPORTED_VERSION)
- && strlen(key) == WS_CLIENT_KEY_LEN) {
- vncws_send_handshake_response(vs, key);
} else {
- VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
- vnc_client_error(vs);
+ VNC_DEBUG("Websock handshake complete, starting VNC protocol\n");
+ vnc_init_state(vs);
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
-
- g_free(protocols);
- g_free(version);
- g_free(key);
}
-void vncws_encode_frame(Buffer *output, const void *payload,
- const size_t payload_size)
-{
- size_t header_size = 0;
- unsigned char opcode = WS_OPCODE_BINARY_FRAME;
- union {
- char buf[WS_HEAD_MAX_LEN];
- WsHeader ws;
- } header;
-
- if (!payload_size) {
- return;
- }
-
- header.ws.b0 = 0x80 | (opcode & 0x0f);
- if (payload_size <= 125) {
- header.ws.b1 = (uint8_t)payload_size;
- header_size = 2;
- } else if (payload_size < 65536) {
- header.ws.b1 = 0x7e;
- header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size);
- header_size = 4;
- } else {
- header.ws.b1 = 0x7f;
- header.ws.u.s64.l64 = cpu_to_be64(payload_size);
- header_size = 10;
- }
-
- buffer_reserve(output, header_size + payload_size);
- buffer_append(output, header.buf, header_size);
- buffer_append(output, payload, payload_size);
-}
-int vncws_decode_frame_header(Buffer *input,
- size_t *header_size,
- size_t *payload_remain,
- WsMask *payload_mask)
+gboolean vncws_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
+ GIOCondition condition G_GNUC_UNUSED,
+ void *opaque)
{
- unsigned char opcode = 0, fin = 0, has_mask = 0;
- size_t payload_len;
- WsHeader *header = (WsHeader *)input->buffer;
-
- if (input->offset < WS_HEAD_MIN_LEN + 4) {
- /* header not complete */
- return 0;
- }
-
- fin = (header->b0 & 0x80) >> 7;
- opcode = header->b0 & 0x0f;
- has_mask = (header->b1 & 0x80) >> 7;
- payload_len = header->b1 & 0x7f;
-
- if (opcode == WS_OPCODE_CLOSE) {
- /* disconnect */
- return -1;
- }
+ VncState *vs = opaque;
+ QIOChannelWebsock *wioc;
- /* Websocket frame sanity check:
- * * Websocket fragmentation is not supported.
- * * All websockets frames sent by a client have to be masked.
- * * Only binary encoding is supported.
- */
- if (!fin || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) {
- VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
- return -2;
+ VNC_DEBUG("Websocket negotiate starting\n");
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ vs->ioc_tag = 0;
}
- if (payload_len < 126) {
- *payload_remain = payload_len;
- *header_size = 6;
- *payload_mask = header->u.m;
- } else if (payload_len == 126 && input->offset >= 8) {
- *payload_remain = be16_to_cpu(header->u.s16.l16);
- *header_size = 8;
- *payload_mask = header->u.s16.m16;
- } else if (payload_len == 127 && input->offset >= 14) {
- *payload_remain = be64_to_cpu(header->u.s64.l64);
- *header_size = 14;
- *payload_mask = header->u.s64.m64;
- } else {
- /* header not complete */
- return 0;
- }
+ wioc = qio_channel_websock_new_server(vs->ioc);
- return 1;
-}
+ object_unref(OBJECT(vs->ioc));
+ vs->ioc = QIO_CHANNEL(wioc);
-int vncws_decode_frame_payload(Buffer *input,
- size_t *payload_remain, WsMask *payload_mask,
- uint8_t **payload, size_t *payload_size)
-{
- size_t i;
- uint32_t *payload32;
-
- *payload = input->buffer;
- /* If we aren't at the end of the payload, then drop
- * off the last bytes, so we're always multiple of 4
- * for purpose of unmasking, except at end of payload
- */
- if (input->offset < *payload_remain) {
- *payload_size = input->offset - (input->offset % 4);
- } else {
- *payload_size = *payload_remain;
- }
- if (*payload_size == 0) {
- return 0;
- }
- *payload_remain -= *payload_size;
-
- /* unmask frame */
- /* process 1 frame (32 bit op) */
- payload32 = (uint32_t *)(*payload);
- for (i = 0; i < *payload_size / 4; i++) {
- payload32[i] ^= payload_mask->u;
- }
- /* process the remaining bytes (if any) */
- for (i *= 4; i < *payload_size; i++) {
- (*payload)[i] ^= payload_mask->c[i % 4];
- }
+ qio_channel_websock_handshake(wioc,
+ vncws_handshake_done,
+ vs,
+ NULL);
- return 1;
+ return TRUE;
}
diff --git a/qemu/ui/vnc-ws.h b/qemu/ui/vnc-ws.h
index 94942258e..652b6fc39 100644
--- a/qemu/ui/vnc-ws.h
+++ b/qemu/ui/vnc-ws.h
@@ -21,72 +21,11 @@
#ifndef __QEMU_UI_VNC_WS_H
#define __QEMU_UI_VNC_WS_H
-#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
-#define SHA1_DIGEST_LEN 20
-
-#define WS_ACCEPT_LEN (B64LEN(SHA1_DIGEST_LEN) + 1)
-#define WS_CLIENT_KEY_LEN 24
-#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-#define WS_GUID_LEN strlen(WS_GUID)
-
-#define WS_HANDSHAKE "HTTP/1.1 101 Switching Protocols\r\n\
-Upgrade: websocket\r\n\
-Connection: Upgrade\r\n\
-Sec-WebSocket-Accept: %s\r\n\
-Sec-WebSocket-Protocol: binary\r\n\
-\r\n"
-#define WS_HANDSHAKE_DELIM "\r\n"
-#define WS_HANDSHAKE_END "\r\n\r\n"
-#define WS_SUPPORTED_VERSION "13"
-
-#define WS_HEAD_MIN_LEN sizeof(uint16_t)
-#define WS_HEAD_MAX_LEN (WS_HEAD_MIN_LEN + sizeof(uint64_t) + sizeof(uint32_t))
-
-typedef union WsMask {
- char c[4];
- uint32_t u;
-} WsMask;
-
-typedef struct QEMU_PACKED WsHeader {
- unsigned char b0;
- unsigned char b1;
- union {
- struct QEMU_PACKED {
- uint16_t l16;
- WsMask m16;
- } s16;
- struct QEMU_PACKED {
- uint64_t l64;
- WsMask m64;
- } s64;
- WsMask m;
- } u;
-} WsHeader;
-
-enum {
- WS_OPCODE_CONTINUATION = 0x0,
- WS_OPCODE_TEXT_FRAME = 0x1,
- WS_OPCODE_BINARY_FRAME = 0x2,
- WS_OPCODE_CLOSE = 0x8,
- WS_OPCODE_PING = 0x9,
- WS_OPCODE_PONG = 0xA
-};
-
-#ifdef CONFIG_VNC_TLS
-void vncws_tls_handshake_io(void *opaque);
-#endif /* CONFIG_VNC_TLS */
-void vncws_handshake_read(void *opaque);
-long vnc_client_write_ws(VncState *vs);
-long vnc_client_read_ws(VncState *vs);
-void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size);
-void vncws_encode_frame(Buffer *output, const void *payload,
- const size_t payload_size);
-int vncws_decode_frame_header(Buffer *input,
- size_t *header_size,
- size_t *payload_remain,
- WsMask *payload_mask);
-int vncws_decode_frame_payload(Buffer *input,
- size_t *payload_remain, WsMask *payload_mask,
- uint8_t **payload, size_t *payload_size);
+gboolean vncws_tls_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque);
+gboolean vncws_handshake_io(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque);
#endif /* __QEMU_UI_VNC_WS_H */
diff --git a/qemu/ui/vnc.c b/qemu/ui/vnc.c
index caf82f56f..d2ebf1fb7 100644
--- a/qemu/ui/vnc.c
+++ b/qemu/ui/vnc.c
@@ -24,6 +24,7 @@
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "vnc.h"
#include "vnc-jobs.h"
#include "trace.h"
@@ -37,10 +38,13 @@
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/types.h"
#include "qmp-commands.h"
-#include "qemu/osdep.h"
#include "ui/input.h"
#include "qapi-event.h"
#include "crypto/hash.h"
+#include "crypto/tlscredsanon.h"
+#include "crypto/tlscredsx509.h"
+#include "qom/object_interfaces.h"
+#include "qemu/cutils.h"
#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
#define VNC_REFRESH_INTERVAL_INC 50
@@ -67,8 +71,8 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
[VNC_SHARE_MODE_EXCLUSIVE] = "exclusive",
[VNC_SHARE_MODE_DISCONNECTED] = "disconnected",
};
- fprintf(stderr, "%s/%d: %s -> %s\n", __func__,
- vs->csock, mn[vs->share_mode], mn[mode]);
+ fprintf(stderr, "%s/%p: %s -> %s\n", __func__,
+ vs->ioc, mn[vs->share_mode], mn[mode]);
#endif
switch (vs->share_mode) {
@@ -102,105 +106,65 @@ static void vnc_set_share_mode(VncState *vs, VncShareMode mode)
}
}
-static char *addr_to_string(const char *format,
- struct sockaddr_storage *sa,
- socklen_t salen) {
- char *addr;
- char host[NI_MAXHOST];
- char serv[NI_MAXSERV];
- int err;
- size_t addrlen;
-
- if ((err = getnameinfo((struct sockaddr *)sa, salen,
- host, sizeof(host),
- serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
- VNC_DEBUG("Cannot resolve address %d: %s\n",
- err, gai_strerror(err));
- return NULL;
- }
-
- /* Enough for the existing format + the 2 vars we're
- * substituting in. */
- addrlen = strlen(format) + strlen(host) + strlen(serv);
- addr = g_malloc(addrlen + 1);
- snprintf(addr, addrlen, format, host, serv);
- addr[addrlen] = '\0';
- return addr;
-}
-
-
-char *vnc_socket_local_addr(const char *format, int fd) {
- struct sockaddr_storage sa;
- socklen_t salen;
-
- salen = sizeof(sa);
- if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0)
- return NULL;
-
- return addr_to_string(format, &sa, salen);
-}
-
-char *vnc_socket_remote_addr(const char *format, int fd) {
- struct sockaddr_storage sa;
- socklen_t salen;
-
- salen = sizeof(sa);
- if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0)
- return NULL;
+static void vnc_init_basic_info(SocketAddress *addr,
+ VncBasicInfo *info,
+ Error **errp)
+{
+ switch (addr->type) {
+ case SOCKET_ADDRESS_KIND_INET:
+ info->host = g_strdup(addr->u.inet.data->host);
+ info->service = g_strdup(addr->u.inet.data->port);
+ if (addr->u.inet.data->ipv6) {
+ info->family = NETWORK_ADDRESS_FAMILY_IPV6;
+ } else {
+ info->family = NETWORK_ADDRESS_FAMILY_IPV4;
+ }
+ break;
- return addr_to_string(format, &sa, salen);
-}
+ case SOCKET_ADDRESS_KIND_UNIX:
+ info->host = g_strdup("");
+ info->service = g_strdup(addr->u.q_unix.data->path);
+ info->family = NETWORK_ADDRESS_FAMILY_UNIX;
+ break;
-static VncBasicInfo *vnc_basic_info_get(struct sockaddr_storage *sa,
- socklen_t salen)
-{
- VncBasicInfo *info;
- char host[NI_MAXHOST];
- char serv[NI_MAXSERV];
- int err;
-
- if ((err = getnameinfo((struct sockaddr *)sa, salen,
- host, sizeof(host),
- serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
- VNC_DEBUG("Cannot resolve address %d: %s\n",
- err, gai_strerror(err));
- return NULL;
+ default:
+ error_setg(errp, "Unsupported socket kind %d",
+ addr->type);
+ break;
}
- info = g_malloc0(sizeof(VncBasicInfo));
- info->host = g_strdup(host);
- info->service = g_strdup(serv);
- info->family = inet_netfamily(sa->ss_family);
- return info;
+ return;
}
-static VncBasicInfo *vnc_basic_info_get_from_server_addr(int fd)
+static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc,
+ VncBasicInfo *info,
+ Error **errp)
{
- struct sockaddr_storage sa;
- socklen_t salen;
+ SocketAddress *addr = NULL;
- salen = sizeof(sa);
- if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
- return NULL;
+ addr = qio_channel_socket_get_local_address(ioc, errp);
+ if (!addr) {
+ return;
}
- return vnc_basic_info_get(&sa, salen);
+ vnc_init_basic_info(addr, info, errp);
+ qapi_free_SocketAddress(addr);
}
-static VncBasicInfo *vnc_basic_info_get_from_remote_addr(int fd)
+static void vnc_init_basic_info_from_remote_addr(QIOChannelSocket *ioc,
+ VncBasicInfo *info,
+ Error **errp)
{
- struct sockaddr_storage sa;
- socklen_t salen;
+ SocketAddress *addr = NULL;
- salen = sizeof(sa);
- if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
- return NULL;
+ addr = qio_channel_socket_get_remote_address(ioc, errp);
+ if (!addr) {
+ return;
}
- return vnc_basic_info_get(&sa, salen);
+ vnc_init_basic_info(addr, info, errp);
+ qapi_free_SocketAddress(addr);
}
static const char *vnc_auth_name(VncDisplay *vd) {
@@ -222,7 +186,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
case VNC_AUTH_TLS:
return "tls";
case VNC_AUTH_VENCRYPT:
-#ifdef CONFIG_VNC_TLS
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
return "vencrypt+plain";
@@ -245,9 +208,6 @@ static const char *vnc_auth_name(VncDisplay *vd) {
default:
return "vencrypt";
}
-#else
- return "vencrypt";
-#endif
case VNC_AUTH_SASL:
return "sasl";
}
@@ -257,15 +217,18 @@ static const char *vnc_auth_name(VncDisplay *vd) {
static VncServerInfo *vnc_server_info_get(VncDisplay *vd)
{
VncServerInfo *info;
- VncBasicInfo *bi = vnc_basic_info_get_from_server_addr(vd->lsock);
- if (!bi) {
- return NULL;
- }
+ Error *err = NULL;
info = g_malloc(sizeof(*info));
- info->base = bi;
+ vnc_init_basic_info_from_server_addr(vd->lsock,
+ qapi_VncServerInfo_base(info), &err);
info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
+ if (err) {
+ qapi_free_VncServerInfo(info);
+ info = NULL;
+ error_free(err);
+ }
return info;
}
@@ -275,13 +238,12 @@ static void vnc_client_cache_auth(VncState *client)
return;
}
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session &&
- client->tls.dname) {
- client->info->has_x509_dname = true;
- client->info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ client->info->x509_dname =
+ qcrypto_tls_session_get_peer_name(client->tls);
+ client->info->has_x509_dname =
+ client->info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn &&
client->sasl.username) {
@@ -293,11 +255,16 @@ static void vnc_client_cache_auth(VncState *client)
static void vnc_client_cache_addr(VncState *client)
{
- VncBasicInfo *bi = vnc_basic_info_get_from_remote_addr(client->csock);
+ Error *err = NULL;
- if (bi) {
- client->info = g_malloc0(sizeof(*client->info));
- client->info->base = bi;
+ client->info = g_malloc0(sizeof(*client->info));
+ vnc_init_basic_info_from_remote_addr(client->sioc,
+ qapi_VncClientInfo_base(client->info),
+ &err);
+ if (err) {
+ qapi_free_VncClientInfo(client->info);
+ client->info = NULL;
+ error_free(err);
}
}
@@ -308,7 +275,6 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
if (!vs->info) {
return;
}
- g_assert(vs->info->base);
si = vnc_server_info_get(vs->vd);
if (!si) {
@@ -317,7 +283,8 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
switch (event) {
case QAPI_EVENT_VNC_CONNECTED:
- qapi_event_send_vnc_connected(si, vs->info->base, &error_abort);
+ qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info),
+ &error_abort);
break;
case QAPI_EVENT_VNC_INITIALIZED:
qapi_event_send_vnc_initialized(si, vs->info, &error_abort);
@@ -334,36 +301,26 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event)
static VncClientInfo *qmp_query_vnc_client(const VncState *client)
{
- struct sockaddr_storage sa;
- socklen_t salen = sizeof(sa);
- char host[NI_MAXHOST];
- char serv[NI_MAXSERV];
VncClientInfo *info;
+ Error *err = NULL;
- if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
- return NULL;
- }
+ info = g_malloc0(sizeof(*info));
- if (getnameinfo((struct sockaddr *)&sa, salen,
- host, sizeof(host),
- serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ vnc_init_basic_info_from_remote_addr(client->sioc,
+ qapi_VncClientInfo_base(info),
+ &err);
+ if (err) {
+ error_free(err);
+ qapi_free_VncClientInfo(info);
return NULL;
}
- info = g_malloc0(sizeof(*info));
- info->base = g_malloc0(sizeof(*info->base));
- info->base->host = g_strdup(host);
- info->base->service = g_strdup(serv);
- info->base->family = inet_netfamily(sa.ss_family);
- info->base->websocket = client->websocket;
+ info->websocket = client->websocket;
-#ifdef CONFIG_VNC_TLS
- if (client->tls.session && client->tls.dname) {
- info->has_x509_dname = true;
- info->x509_dname = g_strdup(client->tls.dname);
+ if (client->tls) {
+ info->x509_dname = qcrypto_tls_session_get_peer_name(client->tls);
+ info->has_x509_dname = info->x509_dname != NULL;
}
-#endif
#ifdef CONFIG_VNC_SASL
if (client->sasl.conn && client->sasl.username) {
info->has_sasl_username = true;
@@ -407,81 +364,89 @@ VncInfo *qmp_query_vnc(Error **errp)
{
VncInfo *info = g_malloc0(sizeof(*info));
VncDisplay *vd = vnc_display_find(NULL);
+ SocketAddress *addr = NULL;
if (vd == NULL || !vd->enabled) {
info->enabled = false;
} else {
- struct sockaddr_storage sa;
- socklen_t salen = sizeof(sa);
- char host[NI_MAXHOST];
- char serv[NI_MAXSERV];
-
info->enabled = true;
/* for compatibility with the original command */
info->has_clients = true;
info->clients = qmp_query_client_list(vd);
- if (vd->lsock == -1) {
+ if (vd->lsock == NULL) {
return info;
}
- if (getsockname(vd->lsock, (struct sockaddr *)&sa,
- &salen) == -1) {
- error_setg(errp, QERR_UNDEFINED_ERROR);
+ addr = qio_channel_socket_get_local_address(vd->lsock, errp);
+ if (!addr) {
goto out_error;
}
- if (getnameinfo((struct sockaddr *)&sa, salen,
- host, sizeof(host),
- serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
- error_setg(errp, QERR_UNDEFINED_ERROR);
+ switch (addr->type) {
+ case SOCKET_ADDRESS_KIND_INET:
+ info->host = g_strdup(addr->u.inet.data->host);
+ info->service = g_strdup(addr->u.inet.data->port);
+ if (addr->u.inet.data->ipv6) {
+ info->family = NETWORK_ADDRESS_FAMILY_IPV6;
+ } else {
+ info->family = NETWORK_ADDRESS_FAMILY_IPV4;
+ }
+ break;
+
+ case SOCKET_ADDRESS_KIND_UNIX:
+ info->host = g_strdup("");
+ info->service = g_strdup(addr->u.q_unix.data->path);
+ info->family = NETWORK_ADDRESS_FAMILY_UNIX;
+ break;
+
+ default:
+ error_setg(errp, "Unsupported socket kind %d",
+ addr->type);
goto out_error;
}
info->has_host = true;
- info->host = g_strdup(host);
-
info->has_service = true;
- info->service = g_strdup(serv);
-
info->has_family = true;
- info->family = inet_netfamily(sa.ss_family);
info->has_auth = true;
info->auth = g_strdup(vnc_auth_name(vd));
}
+ qapi_free_SocketAddress(addr);
return info;
out_error:
+ qapi_free_SocketAddress(addr);
qapi_free_VncInfo(info);
return NULL;
}
-static VncBasicInfoList *qmp_query_server_entry(int socket,
+static VncBasicInfoList *qmp_query_server_entry(QIOChannelSocket *ioc,
bool websocket,
VncBasicInfoList *prev)
{
VncBasicInfoList *list;
VncBasicInfo *info;
- struct sockaddr_storage sa;
- socklen_t salen = sizeof(sa);
- char host[NI_MAXHOST];
- char serv[NI_MAXSERV];
-
- if (getsockname(socket, (struct sockaddr *)&sa, &salen) < 0 ||
- getnameinfo((struct sockaddr *)&sa, salen,
- host, sizeof(host), serv, sizeof(serv),
- NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ Error *err = NULL;
+ SocketAddress *addr;
+
+ addr = qio_channel_socket_get_local_address(ioc, &err);
+ if (!addr) {
+ error_free(err);
return prev;
}
info = g_new0(VncBasicInfo, 1);
- info->host = g_strdup(host);
- info->service = g_strdup(serv);
- info->family = inet_netfamily(sa.ss_family);
+ vnc_init_basic_info(addr, info, &err);
+ qapi_free_SocketAddress(addr);
+ if (err) {
+ qapi_free_VncBasicInfo(info);
+ error_free(err);
+ return prev;
+ }
info->websocket = websocket;
list = g_new0(VncBasicInfoList, 1);
@@ -513,7 +478,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
break;
case VNC_AUTH_VENCRYPT:
info->auth = VNC_PRIMARY_AUTH_VENCRYPT;
-#ifdef CONFIG_VNC_TLS
info->has_vencrypt = true;
switch (vd->subauth) {
case VNC_AUTH_VENCRYPT_PLAIN:
@@ -547,7 +511,6 @@ static void qmp_query_auth(VncDisplay *vd, VncInfo2 *info)
info->has_vencrypt = false;
break;
}
-#endif
break;
case VNC_AUTH_SASL:
info->auth = VNC_PRIMARY_AUTH_SASL;
@@ -577,13 +540,13 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp)
info->has_display = true;
info->display = g_strdup(dev->id);
}
- if (vd->lsock != -1) {
- info->server = qmp_query_server_entry(vd->lsock, false,
- info->server);
+ if (vd->lsock != NULL) {
+ info->server = qmp_query_server_entry(
+ vd->lsock, false, info->server);
}
- if (vd->lwebsock != -1) {
- info->server = qmp_query_server_entry(vd->lwebsock, true,
- info->server);
+ if (vd->lwebsock != NULL) {
+ info->server = qmp_query_server_entry(
+ vd->lwebsock, true, info->server);
}
item = g_new0(VncInfo2List, 1);
@@ -611,10 +574,25 @@ static void framebuffer_update_request(VncState *vs, int incremental,
static void vnc_refresh(DisplayChangeListener *dcl);
static int vnc_refresh_server_surface(VncDisplay *vd);
+static int vnc_width(VncDisplay *vd)
+{
+ return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
+ VNC_DIRTY_PIXELS_PER_BIT));
+}
+
+static int vnc_height(VncDisplay *vd)
+{
+ return MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
+}
+
static void vnc_set_area_dirty(DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT],
VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT),
- int width, int height,
- int x, int y, int w, int h) {
+ VncDisplay *vd,
+ int x, int y, int w, int h)
+{
+ int width = vnc_width(vd);
+ int height = vnc_height(vd);
+
/* this is needed this to ensure we updated all affected
* blocks if x % VNC_DIRTY_PIXELS_PER_BIT != 0 */
w += (x % VNC_DIRTY_PIXELS_PER_BIT);
@@ -636,10 +614,8 @@ static void vnc_dpy_update(DisplayChangeListener *dcl,
{
VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
struct VncSurface *s = &vd->guest;
- int width = pixman_image_get_width(vd->server);
- int height = pixman_image_get_height(vd->server);
- vnc_set_area_dirty(s->dirty, width, height, x, y, w, h);
+ vnc_set_area_dirty(s->dirty, vd, x, y, w, h);
}
void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
@@ -653,53 +629,10 @@ void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
vnc_write_s32(vs, encoding);
}
-void buffer_reserve(Buffer *buffer, size_t len)
-{
- if ((buffer->capacity - buffer->offset) < len) {
- buffer->capacity += (len + 1024);
- buffer->buffer = g_realloc(buffer->buffer, buffer->capacity);
- }
-}
-
-static int buffer_empty(Buffer *buffer)
-{
- return buffer->offset == 0;
-}
-
-uint8_t *buffer_end(Buffer *buffer)
-{
- return buffer->buffer + buffer->offset;
-}
-
-void buffer_reset(Buffer *buffer)
-{
- buffer->offset = 0;
-}
-
-void buffer_free(Buffer *buffer)
-{
- g_free(buffer->buffer);
- buffer->offset = 0;
- buffer->capacity = 0;
- buffer->buffer = NULL;
-}
-
-void buffer_append(Buffer *buffer, const void *data, size_t len)
-{
- memcpy(buffer->buffer + buffer->offset, data, len);
- buffer->offset += len;
-}
-
-void buffer_advance(Buffer *buf, size_t len)
-{
- memmove(buf->buffer, buf->buffer + len,
- (buf->offset - len));
- buf->offset -= len;
-}
static void vnc_desktop_resize(VncState *vs)
{
- if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
+ if (vs->ioc == NULL || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
return;
}
if (vs->client_width == pixman_image_get_width(vs->vd->server) &&
@@ -752,6 +685,21 @@ void *vnc_server_fb_ptr(VncDisplay *vd, int x, int y)
return ptr;
}
+static void vnc_update_server_surface(VncDisplay *vd)
+{
+ qemu_pixman_image_unref(vd->server);
+ vd->server = NULL;
+
+ if (QTAILQ_EMPTY(&vd->clients)) {
+ return;
+ }
+
+ vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
+ vnc_width(vd),
+ vnc_height(vd),
+ NULL, 0);
+}
+
static void vnc_dpy_switch(DisplayChangeListener *dcl,
DisplaySurface *surface)
{
@@ -760,26 +708,19 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
int width, height;
vnc_abort_display_jobs(vd);
+ vd->ds = surface;
/* server surface */
- qemu_pixman_image_unref(vd->server);
- vd->ds = surface;
- width = MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
- VNC_DIRTY_PIXELS_PER_BIT));
- height = MIN(VNC_MAX_HEIGHT, surface_height(vd->ds));
- vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
- width, height, NULL, 0);
+ vnc_update_server_surface(vd);
/* guest surface */
-#if 0 /* FIXME */
- if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
- console_color_init(ds);
-#endif
qemu_pixman_image_unref(vd->guest.fb);
vd->guest.fb = pixman_image_ref(surface->image);
vd->guest.format = surface->format;
+ width = vnc_width(vd);
+ height = vnc_height(vd);
memset(vd->guest.dirty, 0x00, sizeof(vd->guest.dirty));
- vnc_set_area_dirty(vd->guest.dirty, width, height, 0, 0,
+ vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0,
width, height);
QTAILQ_FOREACH(vs, &vd->clients, next) {
@@ -789,7 +730,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl,
vnc_cursor_define(vs);
}
memset(vs->dirty, 0x00, sizeof(vs->dirty));
- vnc_set_area_dirty(vs->dirty, width, height, 0, 0,
+ vnc_set_area_dirty(vs->dirty, vd, 0, 0,
width, height);
}
}
@@ -879,6 +820,8 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
{
int n = 0;
+ bool encode_raw = false;
+ size_t saved_offs = vs->output.offset;
switch(vs->vnc_encoding) {
case VNC_ENCODING_ZLIB:
@@ -901,10 +844,24 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h);
break;
default:
- vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
- n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ encode_raw = true;
break;
}
+
+ /* If the client has the same pixel format as our internal buffer and
+ * a RAW encoding would need less space fall back to RAW encoding to
+ * save bandwidth and processing power in the client. */
+ if (!encode_raw && vs->write_pixels == vnc_write_pixels_copy &&
+ 12 + h * w * VNC_SERVER_FB_BYTES <= (vs->output.offset - saved_offs)) {
+ vs->output.offset = saved_offs;
+ encode_raw = true;
+ }
+
+ if (encode_raw) {
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW);
+ n = vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ }
+
return n;
}
@@ -933,6 +890,11 @@ static void vnc_dpy_copy(DisplayChangeListener *dcl,
int i, x, y, pitch, inc, w_lim, s;
int cmp_bytes;
+ if (!vd->server) {
+ /* no client connected */
+ return;
+ }
+
vnc_refresh_server_surface(vd);
QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
@@ -1063,7 +1025,7 @@ static int find_and_clear_dirty_height(VncState *vs,
static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
{
vs->has_dirty += has_dirty;
- if (vs->need_update && vs->csock != -1) {
+ if (vs->need_update && vs->ioc != NULL) {
VncDisplay *vd = vs->vd;
VncJob *job;
int y;
@@ -1127,7 +1089,7 @@ static int vnc_update_client(VncState *vs, int has_dirty, bool sync)
return n;
}
- if (vs->csock == -1) {
+ if (vs->disconnecting) {
vnc_disconnect_finish(vs);
} else if (sync) {
vnc_jobs_join(vs);
@@ -1209,12 +1171,15 @@ static void audio_del(VncState *vs)
static void vnc_disconnect_start(VncState *vs)
{
- if (vs->csock == -1)
+ if (vs->disconnecting) {
return;
+ }
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
- qemu_set_fd_handler(vs->csock, NULL, NULL, NULL);
- closesocket(vs->csock);
- vs->csock = -1;
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ qio_channel_close(vs->ioc, NULL);
+ vs->disconnecting = TRUE;
}
void vnc_disconnect_finish(VncState *vs)
@@ -1228,8 +1193,6 @@ void vnc_disconnect_finish(VncState *vs)
buffer_free(&vs->input);
buffer_free(&vs->output);
- buffer_free(&vs->ws_input);
- buffer_free(&vs->ws_output);
qapi_free_VncClientInfo(vs->info);
@@ -1237,9 +1200,6 @@ void vnc_disconnect_finish(VncState *vs)
vnc_tight_clear(vs);
vnc_zrle_clear(vs);
-#ifdef CONFIG_VNC_TLS
- vnc_tls_client_cleanup(vs);
-#endif /* CONFIG_VNC_TLS */
#ifdef CONFIG_VNC_SASL
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
@@ -1249,6 +1209,10 @@ void vnc_disconnect_finish(VncState *vs)
if (vs->initialized) {
QTAILQ_REMOVE(&vs->vd->clients, vs, next);
qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
+ if (QTAILQ_EMPTY(&vs->vd->clients)) {
+ /* last client gone */
+ vnc_update_server_surface(vs->vd);
+ }
}
if (vs->vd->lock_key_sync)
@@ -1265,29 +1229,29 @@ void vnc_disconnect_finish(VncState *vs)
g_free(vs->lossy_rect[i]);
}
g_free(vs->lossy_rect);
+
+ object_unref(OBJECT(vs->ioc));
+ vs->ioc = NULL;
+ object_unref(OBJECT(vs->sioc));
+ vs->sioc = NULL;
g_free(vs);
}
-int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp)
{
- if (ret == 0 || ret == -1) {
- if (ret == -1) {
- switch (last_errno) {
- case EINTR:
- case EAGAIN:
-#ifdef _WIN32
- case WSAEWOULDBLOCK:
-#endif
- return 0;
- default:
- break;
- }
+ if (ret <= 0) {
+ if (ret == 0) {
+ VNC_DEBUG("Closing down client sock: EOF\n");
+ } else if (ret != QIO_CHANNEL_ERR_BLOCK) {
+ VNC_DEBUG("Closing down client sock: ret %d (%s)\n",
+ ret, errp ? error_get_pretty(*errp) : "Unknown");
}
- VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
- ret, ret < 0 ? last_errno : 0);
vnc_disconnect_start(vs);
-
+ if (errp) {
+ error_free(*errp);
+ *errp = NULL;
+ }
return 0;
}
return ret;
@@ -1300,23 +1264,6 @@ void vnc_client_error(VncState *vs)
vnc_disconnect_start(vs);
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_write_tls(gnutls_session_t *session,
- const uint8_t *data,
- size_t datalen)
-{
- long ret = gnutls_write(*session, data, datalen);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
- }
- ret = -1;
- }
- return ret;
-}
-#endif /* CONFIG_VNC_TLS */
/*
* Called to write a chunk of data to the client socket. The data may
@@ -1333,20 +1280,14 @@ static long vnc_client_write_tls(gnutls_session_t *session,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_write_tls(&vs->tls.session, data, datalen);
- } else {
-#endif /* CONFIG_VNC_TLS */
- ret = send(vs->csock, (const void *)data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
- }
-#endif /* CONFIG_VNC_TLS */
+ Error *err = NULL;
+ ssize_t ret;
+ ret = qio_channel_write(
+ vs->ioc, (const char *)data, datalen, &err);
VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, &err);
}
@@ -1360,9 +1301,9 @@ long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen)
* the buffered output data if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-static long vnc_client_write_plain(VncState *vs)
+static ssize_t vnc_client_write_plain(VncState *vs)
{
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
@@ -1384,7 +1325,11 @@ static long vnc_client_write_plain(VncState *vs)
buffer_advance(&vs->output, ret);
if (vs->output.offset == 0) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
return ret;
@@ -1396,10 +1341,8 @@ static long vnc_client_write_plain(VncState *vs)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring encryption calls)
*/
-static void vnc_client_write_locked(void *opaque)
+static void vnc_client_write_locked(VncState *vs)
{
- VncState *vs = opaque;
-
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn &&
vs->sasl.runSSF &&
@@ -1408,23 +1351,22 @@ static void vnc_client_write_locked(void *opaque)
} else
#endif /* CONFIG_VNC_SASL */
{
- if (vs->encode_ws) {
- vnc_client_write_ws(vs);
- } else {
- vnc_client_write_plain(vs);
- }
+ vnc_client_write_plain(vs);
}
}
-void vnc_client_write(void *opaque)
+static void vnc_client_write(VncState *vs)
{
- VncState *vs = opaque;
vnc_lock_output(vs);
- if (vs->output.offset || vs->ws_output.offset) {
- vnc_client_write_locked(opaque);
- } else if (vs->csock != -1) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+ if (vs->output.offset) {
+ vnc_client_write_locked(vs);
+ } else if (vs->ioc != NULL) {
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
vnc_unlock_output(vs);
}
@@ -1435,22 +1377,6 @@ void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
vs->read_handler_expect = expecting;
}
-#ifdef CONFIG_VNC_TLS
-static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
- size_t datalen)
-{
- long ret = gnutls_read(*session, data, datalen);
- if (ret < 0) {
- if (ret == GNUTLS_E_AGAIN) {
- errno = EAGAIN;
- } else {
- errno = EIO;
- }
- ret = -1;
- }
- return ret;
-}
-#endif /* CONFIG_VNC_TLS */
/*
* Called to read a chunk of data from the client socket. The data may
@@ -1467,20 +1393,14 @@ static long vnc_client_read_tls(gnutls_session_t *session, uint8_t *data,
* the requested 'datalen' if the socket would block. Returns
* -1 on error, and disconnects the client socket.
*/
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
{
- long ret;
-#ifdef CONFIG_VNC_TLS
- if (vs->tls.session) {
- ret = vnc_client_read_tls(&vs->tls.session, data, datalen);
- } else {
-#endif /* CONFIG_VNC_TLS */
- ret = qemu_recv(vs->csock, data, datalen, 0);
-#ifdef CONFIG_VNC_TLS
- }
-#endif /* CONFIG_VNC_TLS */
+ ssize_t ret;
+ Error *err = NULL;
+ ret = qio_channel_read(
+ vs->ioc, (char *)data, datalen, &err);
VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
- return vnc_client_io_error(vs, ret, socket_error());
+ return vnc_client_io_error(vs, ret, &err);
}
@@ -1492,9 +1412,9 @@ long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen)
* Returns the number of bytes read. Returns -1 on error, and
* disconnects the client socket.
*/
-static long vnc_client_read_plain(VncState *vs)
+static ssize_t vnc_client_read_plain(VncState *vs)
{
- int ret;
+ ssize_t ret;
VNC_DEBUG("Read plain %p size %zd offset %zd\n",
vs->input.buffer, vs->input.capacity, vs->input.offset);
buffer_reserve(&vs->input, 4096);
@@ -1517,31 +1437,20 @@ static void vnc_jobs_bh(void *opaque)
* the client socket. Will delegate actual work according to whether
* SASL SSF layers are enabled (thus requiring decryption calls)
*/
-void vnc_client_read(void *opaque)
+static void vnc_client_read(VncState *vs)
{
- VncState *vs = opaque;
- long ret;
+ ssize_t ret;
#ifdef CONFIG_VNC_SASL
if (vs->sasl.conn && vs->sasl.runSSF)
ret = vnc_client_read_sasl(vs);
else
#endif /* CONFIG_VNC_SASL */
- if (vs->encode_ws) {
- ret = vnc_client_read_ws(vs);
- if (ret == -1) {
- vnc_disconnect_start(vs);
- return;
- } else if (ret == -2) {
- vnc_client_error(vs);
- return;
- }
- } else {
- ret = vnc_client_read_plain(vs);
- }
+ ret = vnc_client_read_plain(vs);
if (!ret) {
- if (vs->csock == -1)
+ if (vs->disconnecting) {
vnc_disconnect_finish(vs);
+ }
return;
}
@@ -1550,7 +1459,7 @@ void vnc_client_read(void *opaque)
int ret;
ret = vs->read_handler(vs, vs->input.buffer, len);
- if (vs->csock == -1) {
+ if (vs->disconnecting) {
vnc_disconnect_finish(vs);
return;
}
@@ -1563,12 +1472,30 @@ void vnc_client_read(void *opaque)
}
}
+gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED,
+ GIOCondition condition, void *opaque)
+{
+ VncState *vs = opaque;
+ if (condition & G_IO_IN) {
+ vnc_client_read(vs);
+ }
+ if (condition & G_IO_OUT) {
+ vnc_client_write(vs);
+ }
+ return TRUE;
+}
+
+
void vnc_write(VncState *vs, const void *data, size_t len)
{
buffer_reserve(&vs->output, len);
- if (vs->csock != -1 && buffer_empty(&vs->output)) {
- qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs);
+ if (vs->ioc != NULL && buffer_empty(&vs->output)) {
+ if (vs->ioc_tag) {
+ g_source_remove(vs->ioc_tag);
+ }
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
}
buffer_append(&vs->output, data, len);
@@ -1609,8 +1536,7 @@ void vnc_write_u8(VncState *vs, uint8_t value)
void vnc_flush(VncState *vs)
{
vnc_lock_output(vs);
- if (vs->csock != -1 && (vs->output.offset ||
- vs->ws_output.offset)) {
+ if (vs->ioc != NULL && vs->output.offset) {
vnc_client_write_locked(vs);
}
vnc_unlock_output(vs);
@@ -1664,7 +1590,7 @@ static void check_pointer_type_change(Notifier *notifier, void *data)
static void pointer_event(VncState *vs, int button_mask, int x, int y)
{
- static uint32_t bmap[INPUT_BUTTON_MAX] = {
+ static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = 0x01,
[INPUT_BUTTON_MIDDLE] = 0x02,
[INPUT_BUTTON_RIGHT] = 0x04,
@@ -2024,9 +1950,6 @@ static void ext_key_event(VncState *vs, int down,
static void framebuffer_update_request(VncState *vs, int incremental,
int x, int y, int w, int h)
{
- int width = pixman_image_get_width(vs->vd->server);
- int height = pixman_image_get_height(vs->vd->server);
-
vs->need_update = 1;
if (incremental) {
@@ -2034,7 +1957,7 @@ static void framebuffer_update_request(VncState *vs, int incremental,
}
vs->force_update = 1;
- vnc_set_area_dirty(vs->dirty, width, height, x, y, w, h);
+ vnc_set_area_dirty(vs->dirty, vs->vd, x, y, w, h);
}
static void send_ext_key_event_ack(VncState *vs)
@@ -2124,6 +2047,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
break;
case VNC_ENCODING_RICH_CURSOR:
vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
+ if (vs->vd->cursor) {
+ vnc_cursor_define(vs);
+ }
break;
case VNC_ENCODING_EXT_KEY_EVENT:
send_ext_key_event_ack(vs);
@@ -2189,15 +2115,15 @@ static void set_pixel_format(VncState *vs,
return;
}
- vs->client_pf.rmax = red_max;
+ vs->client_pf.rmax = red_max ? red_max : 0xFF;
vs->client_pf.rbits = hweight_long(red_max);
vs->client_pf.rshift = red_shift;
vs->client_pf.rmask = red_max << red_shift;
- vs->client_pf.gmax = green_max;
+ vs->client_pf.gmax = green_max ? green_max : 0xFF;
vs->client_pf.gbits = hweight_long(green_max);
vs->client_pf.gshift = green_shift;
vs->client_pf.gmask = green_max << green_shift;
- vs->client_pf.bmax = blue_max;
+ vs->client_pf.bmax = blue_max ? blue_max : 0xFF;
vs->client_pf.bbits = hweight_long(blue_max);
vs->client_pf.bshift = blue_shift;
vs->client_pf.bmask = blue_max << blue_shift;
@@ -2631,12 +2557,10 @@ static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
start_auth_vnc(vs);
break;
-#ifdef CONFIG_VNC_TLS
case VNC_AUTH_VENCRYPT:
VNC_DEBUG("Accept VeNCrypt auth\n");
start_auth_vencrypt(vs);
break;
-#endif /* CONFIG_VNC_TLS */
#ifdef CONFIG_VNC_SASL
case VNC_AUTH_SASL:
@@ -2999,15 +2923,36 @@ static void vnc_refresh(DisplayChangeListener *dcl)
}
}
-static void vnc_connect(VncDisplay *vd, int csock,
+static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
bool skipauth, bool websocket)
{
- VncState *vs = g_malloc0(sizeof(VncState));
+ VncState *vs = g_new0(VncState, 1);
int i;
- vs->csock = csock;
+ vs->sioc = sioc;
+ object_ref(OBJECT(vs->sioc));
+ vs->ioc = QIO_CHANNEL(sioc);
+ object_ref(OBJECT(vs->ioc));
vs->vd = vd;
+ buffer_init(&vs->input, "vnc-input/%p", sioc);
+ buffer_init(&vs->output, "vnc-output/%p", sioc);
+ buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc);
+
+ buffer_init(&vs->tight.tight, "vnc-tight/%p", sioc);
+ buffer_init(&vs->tight.zlib, "vnc-tight-zlib/%p", sioc);
+ buffer_init(&vs->tight.gradient, "vnc-tight-gradient/%p", sioc);
+#ifdef CONFIG_VNC_JPEG
+ buffer_init(&vs->tight.jpeg, "vnc-tight-jpeg/%p", sioc);
+#endif
+#ifdef CONFIG_VNC_PNG
+ buffer_init(&vs->tight.png, "vnc-tight-png/%p", sioc);
+#endif
+ buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc);
+ buffer_init(&vs->zrle.zrle, "vnc-zrle/%p", sioc);
+ buffer_init(&vs->zrle.fb, "vnc-zrle-fb/%p", sioc);
+ buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc);
+
if (skipauth) {
vs->auth = VNC_AUTH_NONE;
vs->subauth = VNC_AUTH_INVALID;
@@ -3020,30 +2965,29 @@ static void vnc_connect(VncDisplay *vd, int csock,
vs->subauth = vd->subauth;
}
}
- VNC_DEBUG("Client sock=%d ws=%d auth=%d subauth=%d\n",
- csock, websocket, vs->auth, vs->subauth);
+ VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n",
+ sioc, websocket, vs->auth, vs->subauth);
vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect));
for (i = 0; i < VNC_STAT_ROWS; ++i) {
- vs->lossy_rect[i] = g_malloc0(VNC_STAT_COLS * sizeof (uint8_t));
+ vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS);
}
- VNC_DEBUG("New client on socket %d\n", csock);
+ VNC_DEBUG("New client on socket %p\n", vs->sioc);
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
- qemu_set_nonblock(vs->csock);
+ qio_channel_set_blocking(vs->ioc, false, NULL);
if (websocket) {
vs->websocket = 1;
-#ifdef CONFIG_VNC_TLS
if (vd->ws_tls) {
- qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs);
- } else
-#endif /* CONFIG_VNC_TLS */
- {
- qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs);
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vncws_tls_handshake_io, vs, NULL);
+ } else {
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vncws_handshake_io, vs, NULL);
}
- } else
- {
- qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs);
+ } else {
+ vs->ioc_tag = qio_channel_add_watch(
+ vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
}
vnc_client_cache_addr(vs);
@@ -3068,6 +3012,7 @@ void vnc_init_state(VncState *vs)
{
vs->initialized = true;
VncDisplay *vd = vs->vd;
+ bool first_client = QTAILQ_EMPTY(&vd->clients);
vs->last_x = -1;
vs->last_y = -1;
@@ -3081,6 +3026,9 @@ void vnc_init_state(VncState *vs)
vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
QTAILQ_INSERT_TAIL(&vd->clients, vs, next);
+ if (first_client) {
+ vnc_update_server_surface(vd);
+ }
graphic_hw_update(vd->dcl.con);
@@ -3097,35 +3045,28 @@ void vnc_init_state(VncState *vs)
/* vs might be free()ed here */
}
-static void vnc_listen_read(void *opaque, bool websocket)
+static gboolean vnc_listen_io(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque)
{
VncDisplay *vs = opaque;
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
- int csock;
+ QIOChannelSocket *sioc = NULL;
+ Error *err = NULL;
/* Catch-up */
graphic_hw_update(vs->dcl.con);
- if (websocket) {
- csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
+ sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), &err);
+ if (sioc != NULL) {
+ qio_channel_set_delay(QIO_CHANNEL(sioc), false);
+ vnc_connect(vs, sioc, false,
+ ioc != QIO_CHANNEL(vs->lsock));
+ object_unref(OBJECT(sioc));
} else {
- csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
- }
-
- if (csock != -1) {
- socket_set_nodelay(csock);
- vnc_connect(vs, csock, false, websocket);
+ /* client probably closed connection before we got there */
+ error_free(err);
}
-}
-static void vnc_listen_regular_read(void *opaque)
-{
- vnc_listen_read(opaque, false);
-}
-
-static void vnc_listen_websocket_read(void *opaque)
-{
- vnc_listen_read(opaque, true);
+ return TRUE;
}
static const DisplayChangeListenerOps dcl_ops = {
@@ -3151,9 +3092,6 @@ void vnc_display_init(const char *id)
vs->id = strdup(id);
QTAILQ_INSERT_TAIL(&vnc_displays, vs, next);
- vs->lsock = -1;
- vs->lwebsock = -1;
-
QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX;
@@ -3181,22 +3119,29 @@ static void vnc_display_close(VncDisplay *vs)
return;
vs->enabled = false;
vs->is_unix = false;
- if (vs->lsock != -1) {
- qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL);
- close(vs->lsock);
- vs->lsock = -1;
+ if (vs->lsock != NULL) {
+ if (vs->lsock_tag) {
+ g_source_remove(vs->lsock_tag);
+ }
+ object_unref(OBJECT(vs->lsock));
+ vs->lsock = NULL;
}
vs->ws_enabled = false;
- if (vs->lwebsock != -1) {
- qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL);
- close(vs->lwebsock);
- vs->lwebsock = -1;
+ if (vs->lwebsock != NULL) {
+ if (vs->lwebsock_tag) {
+ g_source_remove(vs->lwebsock_tag);
+ }
+ object_unref(OBJECT(vs->lwebsock));
+ vs->lwebsock = NULL;
}
vs->auth = VNC_AUTH_INVALID;
vs->subauth = VNC_AUTH_INVALID;
-#ifdef CONFIG_VNC_TLS
- vs->tls.x509verify = 0;
-#endif
+ if (vs->tlscreds) {
+ object_unparent(OBJECT(vs->tlscreds));
+ vs->tlscreds = NULL;
+ }
+ g_free(vs->tlsaclname);
+ vs->tlsaclname = NULL;
}
int vnc_display_password(const char *id, const char *password)
@@ -3233,9 +3178,26 @@ int vnc_display_pw_expire(const char *id, time_t expires)
char *vnc_display_local_addr(const char *id)
{
VncDisplay *vs = vnc_display_find(id);
+ SocketAddress *addr;
+ char *ret;
+ Error *err = NULL;
assert(vs);
- return vnc_socket_local_addr("%s:%s", vs->lsock);
+
+ addr = qio_channel_socket_get_local_address(vs->lsock, &err);
+ if (!addr) {
+ return NULL;
+ }
+
+ if (addr->type != SOCKET_ADDRESS_KIND_INET) {
+ qapi_free_SocketAddress(addr);
+ return NULL;
+ }
+ ret = g_strdup_printf("%s;%s", addr->u.inet.data->host,
+ addr->u.inet.data->port);
+ qapi_free_SocketAddress(addr);
+
+ return ret;
}
static QemuOptsList qemu_vnc_opts = {
@@ -3250,6 +3212,10 @@ static QemuOptsList qemu_vnc_opts = {
.name = "websocket",
.type = QEMU_OPT_STRING,
},{
+ .name = "tls-creds",
+ .type = QEMU_OPT_STRING,
+ },{
+ /* Deprecated in favour of tls-creds */
.name = "x509",
.type = QEMU_OPT_STRING,
},{
@@ -3286,9 +3252,11 @@ static QemuOptsList qemu_vnc_opts = {
.name = "sasl",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "tls",
.type = QEMU_OPT_BOOL,
},{
+ /* Deprecated in favour of tls-creds */
.name = "x509verify",
.type = QEMU_OPT_STRING,
},{
@@ -3306,13 +3274,12 @@ static QemuOptsList qemu_vnc_opts = {
};
-static void
+static int
vnc_display_setup_auth(VncDisplay *vs,
bool password,
bool sasl,
- bool tls,
- bool x509,
- bool websocket)
+ bool websocket,
+ Error **errp)
{
/*
* We have a choice of 3 authentication options
@@ -3362,17 +3329,24 @@ vnc_display_setup_auth(VncDisplay *vs,
* result has the same security characteristics.
*/
if (password) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS password auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with password auth\n");
@@ -3385,17 +3359,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else if (sasl) {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with SASL auth\n");
@@ -3408,17 +3389,24 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
} else {
- if (tls) {
+ if (vs->tlscreds) {
vs->auth = VNC_AUTH_VENCRYPT;
if (websocket) {
vs->ws_tls = true;
}
- if (x509) {
+ if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_X509)) {
VNC_DEBUG("Initializing VNC server with x509 no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
- } else {
+ } else if (object_dynamic_cast(OBJECT(vs->tlscreds),
+ TYPE_QCRYPTO_TLS_CREDS_ANON)) {
VNC_DEBUG("Initializing VNC server with TLS no auth\n");
vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ } else {
+ error_setg(errp,
+ "Unsupported TLS cred type %s",
+ object_get_typename(OBJECT(vs->tlscreds)));
+ return -1;
}
} else {
VNC_DEBUG("Initializing VNC server with no auth\n");
@@ -3431,34 +3419,72 @@ vnc_display_setup_auth(VncDisplay *vs,
vs->ws_auth = VNC_AUTH_INVALID;
}
}
+ return 0;
}
+
+/*
+ * Handle back compat with old CLI syntax by creating some
+ * suitable QCryptoTLSCreds objects
+ */
+static QCryptoTLSCreds *
+vnc_display_create_creds(bool x509,
+ bool x509verify,
+ const char *dir,
+ const char *id,
+ Error **errp)
+{
+ gchar *credsid = g_strdup_printf("tlsvnc%s", id);
+ Object *parent = object_get_objects_root();
+ Object *creds;
+ Error *err = NULL;
+
+ if (x509) {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_X509,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ "dir", dir,
+ "verify-peer", x509verify ? "yes" : "no",
+ NULL);
+ } else {
+ creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON,
+ parent,
+ credsid,
+ &err,
+ "endpoint", "server",
+ NULL);
+ }
+
+ g_free(credsid);
+
+ if (err) {
+ error_propagate(errp, err);
+ return NULL;
+ }
+
+ return QCRYPTO_TLS_CREDS(creds);
+}
+
+
void vnc_display_open(const char *id, Error **errp)
{
VncDisplay *vs = vnc_display_find(id);
QemuOpts *opts = qemu_opts_find(&qemu_vnc_opts, id);
- QemuOpts *sopts, *wsopts;
+ SocketAddress *saddr = NULL, *wsaddr = NULL;
const char *share, *device_id;
QemuConsole *con;
bool password = false;
bool reverse = false;
const char *vnc;
- const char *has_to;
char *h;
- bool has_ipv4 = false;
- bool has_ipv6 = false;
- const char *websocket;
- bool tls = false, x509 = false;
-#ifdef CONFIG_VNC_TLS
- const char *path;
-#endif
+ const char *credid;
bool sasl = false;
#ifdef CONFIG_VNC_SASL
int saslErr;
#endif
-#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
int acl = 0;
-#endif
int lock_key_sync = 1;
if (!vs) {
@@ -3475,44 +3501,90 @@ void vnc_display_open(const char *id, Error **errp)
return;
}
- sopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
- wsopts = qemu_opts_create(&socket_optslist, NULL, 0, &error_abort);
-
h = strrchr(vnc, ':');
if (h) {
- char *host;
size_t hlen = h - vnc;
- if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
- host = g_strndup(vnc + 1, hlen - 2);
+ const char *websocket = qemu_opt_get(opts, "websocket");
+ int to = qemu_opt_get_number(opts, "to", 0);
+ bool has_ipv4 = qemu_opt_get(opts, "ipv4");
+ bool has_ipv6 = qemu_opt_get(opts, "ipv6");
+ bool ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
+ bool ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
+
+ saddr = g_new0(SocketAddress, 1);
+ if (websocket) {
+ if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
+ error_setg(errp,
+ "SHA1 hash support is required for websockets");
+ goto fail;
+ }
+
+ wsaddr = g_new0(SocketAddress, 1);
+ vs->ws_enabled = true;
+ }
+
+ if (strncmp(vnc, "unix:", 5) == 0) {
+ saddr->type = SOCKET_ADDRESS_KIND_UNIX;
+ saddr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
+ saddr->u.q_unix.data->path = g_strdup(vnc + 5);
+
+ if (vs->ws_enabled) {
+ error_setg(errp, "UNIX sockets not supported with websock");
+ goto fail;
+ }
} else {
- host = g_strndup(vnc, hlen);
+ unsigned long long baseport;
+ InetSocketAddress *inet;
+ saddr->type = SOCKET_ADDRESS_KIND_INET;
+ inet = saddr->u.inet.data = g_new0(InetSocketAddress, 1);
+ if (vnc[0] == '[' && vnc[hlen - 1] == ']') {
+ inet->host = g_strndup(vnc + 1, hlen - 2);
+ } else {
+ inet->host = g_strndup(vnc, hlen);
+ }
+ if (parse_uint_full(h + 1, &baseport, 10) < 0) {
+ error_setg(errp, "can't convert to a number: %s", h + 1);
+ goto fail;
+ }
+ if (baseport > 65535 ||
+ baseport + 5900 > 65535) {
+ error_setg(errp, "port %s out of range", h + 1);
+ goto fail;
+ }
+ inet->port = g_strdup_printf(
+ "%d", (int)baseport + 5900);
+
+ if (to) {
+ inet->has_to = true;
+ inet->to = to + 5900;
+ }
+ inet->ipv4 = ipv4;
+ inet->has_ipv4 = has_ipv4;
+ inet->ipv6 = ipv6;
+ inet->has_ipv6 = has_ipv6;
+
+ if (vs->ws_enabled) {
+ wsaddr->type = SOCKET_ADDRESS_KIND_INET;
+ inet = wsaddr->u.inet.data = g_new0(InetSocketAddress, 1);
+ inet->host = g_strdup(saddr->u.inet.data->host);
+ inet->port = g_strdup(websocket);
+
+ if (to) {
+ inet->has_to = true;
+ inet->to = to;
+ }
+ inet->ipv4 = ipv4;
+ inet->has_ipv4 = has_ipv4;
+ inet->ipv6 = ipv6;
+ inet->has_ipv6 = has_ipv6;
+ }
}
- qemu_opt_set(sopts, "host", host, &error_abort);
- qemu_opt_set(wsopts, "host", host, &error_abort);
- qemu_opt_set(sopts, "port", h+1, &error_abort);
- g_free(host);
} else {
error_setg(errp, "no vnc port specified");
goto fail;
}
- has_to = qemu_opt_get(opts, "to");
- has_ipv4 = qemu_opt_get_bool(opts, "ipv4", false);
- has_ipv6 = qemu_opt_get_bool(opts, "ipv6", false);
- if (has_to) {
- qemu_opt_set(sopts, "to", has_to, &error_abort);
- qemu_opt_set(wsopts, "to", has_to, &error_abort);
- }
- if (has_ipv4) {
- qemu_opt_set(sopts, "ipv4", "on", &error_abort);
- qemu_opt_set(wsopts, "ipv4", "on", &error_abort);
- }
- if (has_ipv6) {
- qemu_opt_set(sopts, "ipv6", "on", &error_abort);
- qemu_opt_set(wsopts, "ipv6", "on", &error_abort);
- }
-
password = qemu_opt_get_bool(opts, "password", false);
if (password) {
if (fips_get_state()) {
@@ -3539,32 +3611,67 @@ void vnc_display_open(const char *id, Error **errp)
goto fail;
}
#endif /* CONFIG_VNC_SASL */
- tls = qemu_opt_get_bool(opts, "tls", false);
-#ifdef CONFIG_VNC_TLS
- path = qemu_opt_get(opts, "x509");
- if (!path) {
- path = qemu_opt_get(opts, "x509verify");
- if (path) {
- vs->tls.x509verify = true;
- }
- }
- if (path) {
- x509 = true;
- if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
- error_setg(errp, "Failed to find x509 certificates/keys in %s",
- path);
+ credid = qemu_opt_get(opts, "tls-creds");
+ if (credid) {
+ Object *creds;
+ if (qemu_opt_get(opts, "tls") ||
+ qemu_opt_get(opts, "x509") ||
+ qemu_opt_get(opts, "x509verify")) {
+ error_setg(errp,
+ "'tls-creds' parameter is mutually exclusive with "
+ "'tls', 'x509' and 'x509verify' parameters");
goto fail;
}
+
+ creds = object_resolve_path_component(
+ object_get_objects_root(), credid);
+ if (!creds) {
+ error_setg(errp, "No TLS credentials with id '%s'",
+ credid);
+ goto fail;
+ }
+ vs->tlscreds = (QCryptoTLSCreds *)
+ object_dynamic_cast(creds,
+ TYPE_QCRYPTO_TLS_CREDS);
+ if (!vs->tlscreds) {
+ error_setg(errp, "Object with id '%s' is not TLS credentials",
+ credid);
+ goto fail;
+ }
+ object_ref(OBJECT(vs->tlscreds));
+
+ if (vs->tlscreds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
+ error_setg(errp,
+ "Expecting TLS credentials with a server endpoint");
+ goto fail;
+ }
+ } else {
+ const char *path;
+ bool tls = false, x509 = false, x509verify = false;
+ tls = qemu_opt_get_bool(opts, "tls", false);
+ if (tls) {
+ path = qemu_opt_get(opts, "x509");
+
+ if (path) {
+ x509 = true;
+ } else {
+ path = qemu_opt_get(opts, "x509verify");
+ if (path) {
+ x509 = true;
+ x509verify = true;
+ }
+ }
+ vs->tlscreds = vnc_display_create_creds(x509,
+ x509verify,
+ path,
+ vs->id,
+ errp);
+ if (!vs->tlscreds) {
+ goto fail;
+ }
+ }
}
-#else /* ! CONFIG_VNC_TLS */
- if (tls) {
- error_setg(errp, "VNC TLS auth requires gnutls support");
- goto fail;
- }
-#endif /* ! CONFIG_VNC_TLS */
-#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL)
acl = qemu_opt_get_bool(opts, "acl", false);
-#endif
share = qemu_opt_get(opts, "share");
if (share) {
@@ -3583,16 +3690,6 @@ void vnc_display_open(const char *id, Error **errp)
}
vs->connections_limit = qemu_opt_get_number(opts, "connections", 32);
- websocket = qemu_opt_get(opts, "websocket");
- if (websocket) {
- vs->ws_enabled = true;
- qemu_opt_set(wsopts, "port", websocket, &error_abort);
- if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) {
- error_setg(errp, "SHA1 hash support is required for websockets");
- goto fail;
- }
- }
-
#ifdef CONFIG_VNC_JPEG
vs->lossy = qemu_opt_get_bool(opts, "lossy", false);
#endif
@@ -3604,19 +3701,14 @@ void vnc_display_open(const char *id, Error **errp)
vs->non_adaptive = true;
}
-#ifdef CONFIG_VNC_TLS
- if (acl && x509 && vs->tls.x509verify) {
- char *aclname;
-
+ if (acl) {
if (strcmp(vs->id, "default") == 0) {
- aclname = g_strdup("vnc.x509dname");
+ vs->tlsaclname = g_strdup("vnc.x509dname");
} else {
- aclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
+ vs->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vs->id);
}
- vs->tls.acl = qemu_acl_init(aclname);
- g_free(aclname);
+ qemu_acl_init(vs->tlsaclname);
}
-#endif
#ifdef CONFIG_VNC_SASL
if (acl && sasl) {
char *aclname;
@@ -3631,7 +3723,9 @@ void vnc_display_open(const char *id, Error **errp)
}
#endif
- vnc_display_setup_auth(vs, password, sasl, tls, x509, websocket);
+ if (vnc_display_setup_auth(vs, password, sasl, vs->ws_enabled, errp) < 0) {
+ goto fail;
+ }
#ifdef CONFIG_VNC_SASL
if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
@@ -3644,19 +3738,12 @@ void vnc_display_open(const char *id, Error **errp)
device_id = qemu_opt_get(opts, "display");
if (device_id) {
- DeviceState *dev;
int head = qemu_opt_get_number(opts, "head", 0);
+ Error *err = NULL;
- dev = qdev_find_recursive(sysbus_get_default(), device_id);
- if (dev == NULL) {
- error_setg(errp, "Device '%s' not found", device_id);
- goto fail;
- }
-
- con = qemu_console_lookup_by_device(dev, head);
- if (con == NULL) {
- error_setg(errp, "Device %s is not bound to a QemuConsole",
- device_id);
+ con = qemu_console_lookup_by_device_name(device_id, head, &err);
+ if (err) {
+ error_propagate(errp, err);
goto fail;
}
} else {
@@ -3671,56 +3758,55 @@ void vnc_display_open(const char *id, Error **errp)
if (reverse) {
/* connect to viewer */
- int csock;
- vs->lsock = -1;
- vs->lwebsock = -1;
- if (strncmp(vnc, "unix:", 5) == 0) {
- csock = unix_connect(vnc+5, errp);
- } else {
- csock = inet_connect(vnc, errp);
+ QIOChannelSocket *sioc = NULL;
+ vs->lsock = NULL;
+ vs->lwebsock = NULL;
+ if (vs->ws_enabled) {
+ error_setg(errp, "Cannot use websockets in reverse mode");
+ goto fail;
}
- if (csock < 0) {
+ vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
+ sioc = qio_channel_socket_new();
+ if (qio_channel_socket_connect_sync(sioc, saddr, errp) < 0) {
goto fail;
}
- vnc_connect(vs, csock, false, false);
+ vnc_connect(vs, sioc, false, false);
+ object_unref(OBJECT(sioc));
} else {
- /* listen for connects */
- if (strncmp(vnc, "unix:", 5) == 0) {
- vs->lsock = unix_listen(vnc+5, NULL, 0, errp);
- if (vs->lsock < 0) {
- goto fail;
- }
- vs->is_unix = true;
- } else {
- vs->lsock = inet_listen_opts(sopts, 5900, errp);
- if (vs->lsock < 0) {
+ vs->lsock = qio_channel_socket_new();
+ if (qio_channel_socket_listen_sync(vs->lsock, saddr, errp) < 0) {
+ goto fail;
+ }
+ vs->is_unix = saddr->type == SOCKET_ADDRESS_KIND_UNIX;
+ vs->enabled = true;
+
+ if (vs->ws_enabled) {
+ vs->lwebsock = qio_channel_socket_new();
+ if (qio_channel_socket_listen_sync(vs->lwebsock,
+ wsaddr, errp) < 0) {
+ object_unref(OBJECT(vs->lsock));
+ vs->lsock = NULL;
goto fail;
}
- if (vs->ws_enabled) {
- vs->lwebsock = inet_listen_opts(wsopts, 0, errp);
- if (vs->lwebsock < 0) {
- if (vs->lsock != -1) {
- close(vs->lsock);
- vs->lsock = -1;
- }
- goto fail;
- }
- }
}
- vs->enabled = true;
- qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs);
+
+ vs->lsock_tag = qio_channel_add_watch(
+ QIO_CHANNEL(vs->lsock),
+ G_IO_IN, vnc_listen_io, vs, NULL);
if (vs->ws_enabled) {
- qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read,
- NULL, vs);
+ vs->lwebsock_tag = qio_channel_add_watch(
+ QIO_CHANNEL(vs->lwebsock),
+ G_IO_IN, vnc_listen_io, vs, NULL);
}
}
- qemu_opts_del(sopts);
- qemu_opts_del(wsopts);
+
+ qapi_free_SocketAddress(saddr);
+ qapi_free_SocketAddress(wsaddr);
return;
fail:
- qemu_opts_del(sopts);
- qemu_opts_del(wsopts);
+ qapi_free_SocketAddress(saddr);
+ qapi_free_SocketAddress(wsaddr);
vs->enabled = false;
vs->ws_enabled = false;
}
@@ -3728,11 +3814,17 @@ fail:
void vnc_display_add_client(const char *id, int csock, bool skipauth)
{
VncDisplay *vs = vnc_display_find(id);
+ QIOChannelSocket *sioc;
if (!vs) {
return;
}
- vnc_connect(vs, csock, skipauth, false);
+
+ sioc = qio_channel_socket_new_fd(csock, NULL);
+ if (sioc) {
+ vnc_connect(vs, sioc, skipauth, false);
+ object_unref(OBJECT(sioc));
+ }
}
static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
@@ -3775,9 +3867,7 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
vnc_display_init(id);
vnc_display_open(id, &local_err);
if (local_err != NULL) {
- error_report("Failed to start VNC server: %s",
- error_get_pretty(local_err));
- error_free(local_err);
+ error_reportf_err(local_err, "Failed to start VNC server: ");
exit(1);
}
return 0;
@@ -3787,4 +3877,4 @@ static void vnc_register_config(void)
{
qemu_add_opts(&qemu_vnc_opts);
}
-machine_init(vnc_register_config);
+opts_init(vnc_register_config);
diff --git a/qemu/ui/vnc.h b/qemu/ui/vnc.h
index 814d720df..81a326116 100644
--- a/qemu/ui/vnc.h
+++ b/qemu/ui/vnc.h
@@ -33,8 +33,11 @@
#include "ui/console.h"
#include "audio/audio.h"
#include "qemu/bitmap.h"
+#include "crypto/tlssession.h"
+#include "qemu/buffer.h"
+#include "io/channel-socket.h"
+#include "io/channel-tls.h"
#include <zlib.h>
-#include <stdbool.h>
#include "keymaps.h"
#include "vnc-palette.h"
@@ -55,13 +58,6 @@
*
*****************************************************************************/
-typedef struct Buffer
-{
- size_t capacity;
- size_t offset;
- uint8_t *buffer;
-} Buffer;
-
typedef struct VncState VncState;
typedef struct VncJob VncJob;
typedef struct VncRect VncRect;
@@ -101,10 +97,7 @@ typedef void VncSendHextileTile(VncState *vs,
typedef struct VncDisplay VncDisplay;
-#ifdef CONFIG_VNC_TLS
-#include "vnc-tls.h"
#include "vnc-auth-vencrypt.h"
-#endif
#ifdef CONFIG_VNC_SASL
#include "vnc-auth-sasl.h"
#endif
@@ -153,8 +146,10 @@ struct VncDisplay
int num_exclusive;
int connections_limit;
VncSharePolicy share_policy;
- int lsock;
- int lwebsock;
+ QIOChannelSocket *lsock;
+ guint lsock_tag;
+ QIOChannelSocket *lwebsock;
+ guint lwebsock_tag;
bool ws_enabled;
DisplaySurface *ds;
DisplayChangeListener dcl;
@@ -181,9 +176,8 @@ struct VncDisplay
bool ws_tls; /* Used by websockets */
bool lossy;
bool non_adaptive;
-#ifdef CONFIG_VNC_TLS
- VncDisplayTLS tls;
-#endif
+ QCryptoTLSCreds *tlscreds;
+ char *tlsaclname;
#ifdef CONFIG_VNC_SASL
VncDisplaySASL sasl;
#endif
@@ -257,7 +251,10 @@ struct VncJob
struct VncState
{
- int csock;
+ QIOChannelSocket *sioc; /* The underlying socket */
+ QIOChannel *ioc; /* The channel currently used for I/O */
+ guint ioc_tag;
+ gboolean disconnecting;
DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS);
uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in
@@ -284,9 +281,7 @@ struct VncState
int auth;
int subauth; /* Used by VeNCrypt */
char challenge[VNC_AUTH_CHALLENGE_SIZE];
-#ifdef CONFIG_VNC_TLS
- VncStateTLS tls;
-#endif
+ QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */
#ifdef CONFIG_VNC_SASL
VncStateSASL sasl;
#endif
@@ -297,10 +292,6 @@ struct VncState
Buffer output;
Buffer input;
- Buffer ws_input;
- Buffer ws_output;
- size_t ws_payload_remain;
- WsMask ws_payload_mask;
/* current output mode information */
VncWritePixels *write_pixels;
PixelFormat client_pf;
@@ -510,11 +501,12 @@ enum {
*****************************************************************************/
/* Event loop functions */
-void vnc_client_read(void *opaque);
-void vnc_client_write(void *opaque);
+gboolean vnc_client_io(QIOChannel *ioc,
+ GIOCondition condition,
+ void *opaque);
-long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
-long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
+ssize_t vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen);
+ssize_t vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen);
/* Protocol I/O functions */
void vnc_write(VncState *vs, const void *data, size_t len);
@@ -533,25 +525,14 @@ uint32_t read_u32(uint8_t *data, size_t offset);
/* Protocol stage functions */
void vnc_client_error(VncState *vs);
-int vnc_client_io_error(VncState *vs, int ret, int last_errno);
+ssize_t vnc_client_io_error(VncState *vs, ssize_t ret, Error **errp);
void start_client_init(VncState *vs);
void start_auth_vnc(VncState *vs);
-/* Buffer management */
-void buffer_reserve(Buffer *buffer, size_t len);
-void buffer_reset(Buffer *buffer);
-void buffer_free(Buffer *buffer);
-void buffer_append(Buffer *buffer, const void *data, size_t len);
-void buffer_advance(Buffer *buf, size_t len);
-uint8_t *buffer_end(Buffer *buffer);
-
/* Misc helpers */
-char *vnc_socket_local_addr(const char *format, int fd);
-char *vnc_socket_remote_addr(const char *format, int fd);
-
static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
return (vs->features & (1 << feature));
}
diff --git a/qemu/ui/x_keymap.c b/qemu/ui/x_keymap.c
index 1a773174f..27884851d 100644
--- a/qemu/ui/x_keymap.c
+++ b/qemu/ui/x_keymap.c
@@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "qemu-common.h"
#include "x_keymap.h"