aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorCorentin Chary <corentincj@iksaif.net>2010-07-07 20:57:52 +0200
committerAnthony Liguori <aliguori@us.ibm.com>2010-07-26 17:35:54 -0500
commit3e230dd23b750f19a2fb95cf08250c74432ed164 (patch)
tree9397dccd2f430da4f644fbf83ea11e92ea046641 /ui
parent6f9c78c1499bd2fb3c31bf87d9b7c1b10c188dfb (diff)
ui: move all ui components in ui/
Move sdl, vnc, curses and cocoa UI into ui/ to cleanup the root directory. Also remove some unnecessary explicit targets from Makefile. aliguori: fix build when srcdir != objdir Signed-off-by: Corentin Chary <corentincj@iksaif.net> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'ui')
-rw-r--r--ui/cocoa.m1014
-rw-r--r--ui/curses.c365
-rw-r--r--ui/curses_keys.h508
-rw-r--r--ui/d3des.c424
-rw-r--r--ui/d3des.h51
-rw-r--r--ui/keymaps.c210
-rw-r--r--ui/keymaps.h77
-rw-r--r--ui/sdl.c879
-rw-r--r--ui/sdl_keysym.h277
-rw-r--r--ui/sdl_zoom.c95
-rw-r--r--ui/sdl_zoom.h25
-rw-r--r--ui/sdl_zoom_template.h225
-rw-r--r--ui/vnc-auth-sasl.c637
-rw-r--r--ui/vnc-auth-sasl.h74
-rw-r--r--ui/vnc-auth-vencrypt.c175
-rw-r--r--ui/vnc-auth-vencrypt.h33
-rw-r--r--ui/vnc-encoding-hextile.c116
-rw-r--r--ui/vnc-encoding-tight.c1520
-rw-r--r--ui/vnc-encoding-tight.h181
-rw-r--r--ui/vnc-encoding-zlib.c152
-rw-r--r--ui/vnc-tls.c445
-rw-r--r--ui/vnc-tls.h76
-rw-r--r--ui/vnc.c2631
-rw-r--r--ui/vnc.h430
-rw-r--r--ui/vnc_keysym.h324
-rw-r--r--ui/vnchextile.h211
-rw-r--r--ui/x_keymap.c168
-rw-r--r--ui/x_keymap.h32
28 files changed, 11355 insertions, 0 deletions
diff --git a/ui/cocoa.m b/ui/cocoa.m
new file mode 100644
index 0000000000..56c789a98c
--- /dev/null
+++ b/ui/cocoa.m
@@ -0,0 +1,1014 @@
+/*
+ * QEMU Cocoa CG display driver
+ *
+ * Copyright (c) 2008 Mike Kronenberg
+ *
+ * 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.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+#ifndef MAC_OS_X_VERSION_10_4
+#define MAC_OS_X_VERSION_10_4 1040
+#endif
+#ifndef MAC_OS_X_VERSION_10_5
+#define MAC_OS_X_VERSION_10_5 1050
+#endif
+
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); }
+#else
+#define COCOA_DEBUG(...) ((void) 0)
+#endif
+
+#define cgrect(nsrect) (*(CGRect *)&(nsrect))
+#define COCOA_MOUSE_EVENT \
+ if (isTabletEnabled) { \
+ kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \
+ } else if (isMouseGrabed) { \
+ kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
+ } else { \
+ [NSApp sendEvent:event]; \
+ }
+
+typedef struct {
+ int width;
+ int height;
+ int bitsPerComponent;
+ int bitsPerPixel;
+} QEMUScreen;
+
+int qemu_main(int argc, char **argv); // main defined in qemu/vl.c
+NSWindow *normalWindow;
+id cocoaView;
+static DisplayChangeListener *dcl;
+
+int gArgc;
+char **gArgv;
+
+// 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
+ 0, // 54 0x36 QZ_RMETA
+ 0, // 55 0x37 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 */
+
+/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */
+/*
+ 219 // 0xdb e0,5b L GUI
+ 220 // 0xdc e0,5c R GUI
+ 221 // 0xdd e0,5d APPS
+ // E0,2A,E0,37 PRNT SCRN
+ // E1,1D,45,E1,9D,C5 PAUSE
+ 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
+*/
+};
+
+static int cocoa_keycode_to_qemu(int keycode)
+{
+ if((sizeof(keymap)/sizeof(int)) <= keycode)
+ {
+ printf("(cocoa) warning unknow keycode 0x%x\n", keycode);
+ return 0;
+ }
+ return keymap[keycode];
+}
+
+
+
+/*
+ ------------------------------------------------------
+ QemuCocoaView
+ ------------------------------------------------------
+*/
+@interface QemuCocoaView : NSView
+{
+ QEMUScreen screen;
+ NSWindow *fullScreenWindow;
+ float cx,cy,cw,ch,cdx,cdy;
+ CGDataProviderRef dataProviderRef;
+ int modifiers_state[256];
+ BOOL isMouseGrabed;
+ BOOL isFullscreen;
+ BOOL isAbsoluteEnabled;
+ BOOL isTabletEnabled;
+}
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds;
+- (void) grabMouse;
+- (void) ungrabMouse;
+- (void) toggleFullScreen:(id)sender;
+- (void) handleEvent:(NSEvent *)event;
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
+- (BOOL) isMouseGrabed;
+- (BOOL) isAbsoluteEnabled;
+- (float) cdx;
+- (float) cdy;
+- (QEMUScreen) gscreen;
+@end
+
+@implementation QemuCocoaView
+- (id)initWithFrame:(NSRect)frameRect
+{
+ COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
+
+ self = [super initWithFrame:frameRect];
+ if (self) {
+
+ screen.bitsPerComponent = 8;
+ screen.bitsPerPixel = 32;
+ screen.width = frameRect.size.width;
+ screen.height = frameRect.size.height;
+
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ COCOA_DEBUG("QemuCocoaView: dealloc\n");
+
+ if (dataProviderRef)
+ CGDataProviderRelease(dataProviderRef);
+
+ [super dealloc];
+}
+
+- (BOOL) isOpaque
+{
+ return YES;
+}
+
+- (void) drawRect:(NSRect) rect
+{
+ COCOA_DEBUG("QemuCocoaView: drawRect\n");
+
+ // get CoreGraphic context
+ CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
+ CGContextSetShouldAntialias (viewContextRef, NO);
+
+ // draw screen bitmap directly to Core Graphics context
+ if (dataProviderRef) {
+ CGImageRef imageRef = CGImageCreate(
+ screen.width, //width
+ screen.height, //height
+ screen.bitsPerComponent, //bitsPerComponent
+ screen.bitsPerPixel, //bitsPerPixel
+ (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow
+#ifdef __LITTLE_ENDIAN__
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4
+ kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,
+#else
+ CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
+ kCGImageAlphaNoneSkipFirst, //bitmapInfo
+#endif
+ dataProviderRef, //provider
+ NULL, //decode
+ 0, //interpolate
+ kCGRenderingIntentDefault //intent
+ );
+// test if host supports "CGImageCreateWithImageInRect" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
+#endif
+ // compatibility drawing code (draws everything) (OS X < 10.4)
+ CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+ } else {
+ // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
+ const NSRect *rectList;
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ NSInteger rectCount;
+#else
+ int rectCount;
+#endif
+ int i;
+ CGImageRef clipImageRef;
+ CGRect clipRect;
+
+ [self getRectsBeingDrawn:&rectList count:&rectCount];
+ for (i = 0; i < rectCount; i++) {
+ clipRect.origin.x = rectList[i].origin.x / cdx;
+ clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
+ clipRect.size.width = rectList[i].size.width / cdx;
+ clipRect.size.height = rectList[i].size.height / cdy;
+ clipImageRef = CGImageCreateWithImageInRect(
+ imageRef,
+ clipRect
+ );
+ CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
+ CGImageRelease (clipImageRef);
+ }
+ }
+#endif
+ CGImageRelease (imageRef);
+ }
+}
+
+- (void) setContentDimensions
+{
+ COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");
+
+ if (isFullscreen) {
+ cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width;
+ cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
+ cw = screen.width * cdx;
+ ch = screen.height * cdy;
+ cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0;
+ cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0;
+ } else {
+ cx = 0;
+ cy = 0;
+ cw = screen.width;
+ ch = screen.height;
+ cdx = 1.0;
+ cdy = 1.0;
+ }
+}
+
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds
+{
+ COCOA_DEBUG("QemuCocoaView: resizeContent\n");
+
+ // update screenBuffer
+ if (dataProviderRef)
+ CGDataProviderRelease(dataProviderRef);
+
+ //sync host window color space with guests
+ screen.bitsPerPixel = ds_get_bits_per_pixel(ds);
+ screen.bitsPerComponent = ds_get_bytes_per_pixel(ds) * 2;
+
+ dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), w * 4 * h, NULL);
+
+ // update windows
+ if (isFullscreen) {
+ [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
+ } else {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+ [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO];
+ }
+ screen.width = w;
+ screen.height = h;
+ [normalWindow center];
+ [self setContentDimensions];
+ [self setFrame:NSMakeRect(cx, cy, cw, ch)];
+}
+
+- (void) toggleFullScreen:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
+
+ if (isFullscreen) { // switch from fullscreen to desktop
+ isFullscreen = FALSE;
+ [self ungrabMouse];
+ [self setContentDimensions];
+// test if host supports "exitFullScreenModeWithOptions" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
+ [self exitFullScreenModeWithOptions:nil];
+ } else {
+#endif
+ [fullScreenWindow close];
+ [normalWindow setContentView: self];
+ [normalWindow makeKeyAndOrderFront: self];
+ [NSMenu setMenuBarVisible:YES];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ }
+#endif
+ } else { // switch from desktop to fullscreen
+ isFullscreen = TRUE;
+ [self grabMouse];
+ [self setContentDimensions];
+// test if host supports "enterFullScreenMode:withOptions" at compile time
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime
+ [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
+ [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting,
+ nil]];
+ } else {
+#endif
+ [NSMenu setMenuBarVisible:NO];
+ fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame]
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ [fullScreenWindow setHasShadow:NO];
+ [fullScreenWindow setContentView:self];
+ [fullScreenWindow makeKeyAndOrderFront:self];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
+ }
+#endif
+ }
+}
+
+- (void) handleEvent:(NSEvent *)event
+{
+ COCOA_DEBUG("QemuCocoaView: handleEvent\n");
+
+ int buttons = 0;
+ int keycode;
+ NSPoint p = [event locationInWindow];
+
+ switch ([event type]) {
+ case NSFlagsChanged:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+ if (keycode) {
+ if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | 0x80);
+ } else if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ if (modifiers_state[keycode] == 0) { // keydown
+ kbd_put_keycode(keycode & 0x7f);
+ modifiers_state[keycode] = 1;
+ } else { // keyup
+ kbd_put_keycode(keycode | 0x80);
+ modifiers_state[keycode] = 0;
+ }
+ }
+ }
+
+ // release Mouse grab when pressing ctrl+alt
+ if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+ [self ungrabMouse];
+ }
+ break;
+ case NSKeyDown:
+
+ // forward command Key Combos
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ [NSApp sendEvent:event];
+ return;
+ }
+
+ // default
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+ // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
+ if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+ switch (keycode) {
+
+ // enable graphic console
+ case 0x02 ... 0x0a: // '1' to '9' keys
+ console_select(keycode - 0x02);
+ break;
+ }
+
+ // handle keys for graphic console
+ } else if (is_graphic_console()) {
+ if (keycode & 0x80) //check bit for e0 in front
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+
+ // handlekeys for Monitor
+ } else {
+ int keysym = 0;
+ switch([event keyCode]) {
+ case 115:
+ keysym = QEMU_KEY_HOME;
+ break;
+ case 117:
+ keysym = QEMU_KEY_DELETE;
+ break;
+ case 119:
+ keysym = QEMU_KEY_END;
+ break;
+ case 123:
+ keysym = QEMU_KEY_LEFT;
+ break;
+ case 124:
+ keysym = QEMU_KEY_RIGHT;
+ break;
+ case 125:
+ keysym = QEMU_KEY_DOWN;
+ break;
+ case 126:
+ keysym = QEMU_KEY_UP;
+ break;
+ default:
+ {
+ NSString *ks = [event characters];
+ if ([ks length] > 0)
+ keysym = [ks characterAtIndex:0];
+ }
+ }
+ if (keysym)
+ kbd_put_keysym(keysym);
+ }
+ break;
+ case NSKeyUp:
+ keycode = cocoa_keycode_to_qemu([event keyCode]);
+ if (is_graphic_console()) {
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+ kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+ }
+ break;
+ case NSMouseMoved:
+ if (isAbsoluteEnabled) {
+ if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) {
+ if (isTabletEnabled) { // if we leave the window, deactivate the tablet
+ [NSCursor unhide];
+ isTabletEnabled = FALSE;
+ }
+ } else {
+ if (!isTabletEnabled) { // if we enter the window, activate the tablet
+ [NSCursor hide];
+ isTabletEnabled = TRUE;
+ }
+ }
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseDown:
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSRightMouseDown:
+ buttons |= MOUSE_EVENT_RBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseDown:
+ buttons |= MOUSE_EVENT_MBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseDragged:
+ if ([event modifierFlags] & NSCommandKeyMask) {
+ buttons |= MOUSE_EVENT_RBUTTON;
+ } else {
+ buttons |= MOUSE_EVENT_LBUTTON;
+ }
+ COCOA_MOUSE_EVENT
+ break;
+ case NSRightMouseDragged:
+ buttons |= MOUSE_EVENT_RBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseDragged:
+ buttons |= MOUSE_EVENT_MBUTTON;
+ COCOA_MOUSE_EVENT
+ break;
+ case NSLeftMouseUp:
+ if (isTabletEnabled) {
+ COCOA_MOUSE_EVENT
+ } else if (!isMouseGrabed) {
+ if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
+ [self grabMouse];
+ } else {
+ [NSApp sendEvent:event];
+ }
+ } else {
+ COCOA_MOUSE_EVENT
+ }
+ break;
+ case NSRightMouseUp:
+ COCOA_MOUSE_EVENT
+ break;
+ case NSOtherMouseUp:
+ COCOA_MOUSE_EVENT
+ break;
+ case NSScrollWheel:
+ if (isTabletEnabled || isMouseGrabed) {
+ kbd_mouse_event(0, 0, -[event deltaY], 0);
+ } else {
+ [NSApp sendEvent:event];
+ }
+ break;
+ default:
+ [NSApp sendEvent:event];
+ }
+}
+
+- (void) grabMouse
+{
+ COCOA_DEBUG("QemuCocoaView: grabMouse\n");
+
+ if (!isFullscreen) {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]];
+ else
+ [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"];
+ }
+ [NSCursor hide];
+ CGAssociateMouseAndMouseCursorPosition(FALSE);
+ isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
+}
+
+- (void) ungrabMouse
+{
+ COCOA_DEBUG("QemuCocoaView: ungrabMouse\n");
+
+ if (!isFullscreen) {
+ if (qemu_name)
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+ else
+ [normalWindow setTitle:@"QEMU"];
+ }
+ [NSCursor unhide];
+ CGAssociateMouseAndMouseCursorPosition(TRUE);
+ isMouseGrabed = FALSE;
+}
+
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
+- (BOOL) isMouseGrabed {return isMouseGrabed;}
+- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
+- (float) cdx {return cdx;}
+- (float) cdy {return cdy;}
+- (QEMUScreen) gscreen {return screen;}
+@end
+
+
+
+/*
+ ------------------------------------------------------
+ QemuCocoaAppController
+ ------------------------------------------------------
+*/
+@interface QemuCocoaAppController : NSObject
+{
+}
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+- (void)toggleFullScreen:(id)sender;
+- (void)showQEMUDoc:(id)sender;
+- (void)showQEMUTec:(id)sender;
+@end
+
+@implementation QemuCocoaAppController
+- (id) init
+{
+ COCOA_DEBUG("QemuCocoaAppController: init\n");
+
+ self = [super init];
+ if (self) {
+
+ // create a view and add it to the window
+ cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
+ if(!cocoaView) {
+ fprintf(stderr, "(cocoa) can't create a view\n");
+ exit(1);
+ }
+
+ // create a window
+ normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
+ styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
+ backing:NSBackingStoreBuffered defer:NO];
+ if(!normalWindow) {
+ fprintf(stderr, "(cocoa) can't create window\n");
+ exit(1);
+ }
+ [normalWindow setAcceptsMouseMovedEvents:YES];
+ [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
+ [normalWindow setContentView:cocoaView];
+ [normalWindow useOptimizedDrawing:YES];
+ [normalWindow makeKeyAndOrderFront:self];
+ [normalWindow center];
+
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
+
+ if (cocoaView)
+ [cocoaView release];
+ [super dealloc];
+}
+
+- (void)applicationDidFinishLaunching: (NSNotification *) note
+{
+ COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
+
+ // Display an open dialog box if no argument 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"];
+ [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
+ modalForWindow:normalWindow modalDelegate:self
+ didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
+ } else {
+ // or Launch Qemu, with the global args
+ [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
+ }
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification
+{
+ COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
+
+ qemu_system_shutdown_request();
+ exit(0);
+}
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
+{
+ return YES;
+}
+
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+{
+ COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
+
+ int status;
+ status = qemu_main(argc, argv);
+ exit(status);
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
+
+ if(returnCode == NSCancelButton) {
+ exit(0);
+ } else if(returnCode == NSOKButton) {
+ const char *bin = "qemu";
+ char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding];
+
+ char **argv = (char**)malloc( sizeof(char*)*3 );
+
+ asprintf(&argv[0], "%s", bin);
+ asprintf(&argv[1], "-hda");
+ asprintf(&argv[2], "%s", img);
+
+ printf("Using argc %d argv %s -hda %s\n", 3, bin, img);
+
+ [self startEmulationWithArgc:3 argv:(char**)argv];
+ }
+}
+- (void)toggleFullScreen:(id)sender
+{
+ COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n");
+
+ [cocoaView toggleFullScreen:sender];
+}
+
+- (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"];
+}
+
+- (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"];
+}
+@end
+
+
+
+// Dock Connection
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+int main (int argc, const char * argv[]) {
+
+ gArgc = argc;
+ gArgv = (char **)argv;
+ CPSProcessSerNum PSN;
+ int i;
+
+ /* In case we don't need to display a window, let's not do that */
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-vnc") ||
+ !strcmp(argv[i], "-nographic") ||
+ !strcmp(argv[i], "-curses")) {
+ return qemu_main(gArgc, gArgv);
+ }
+ }
+
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ [NSApplication sharedApplication];
+
+ if (!CPSGetCurrentProcess(&PSN))
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [NSApplication sharedApplication];
+
+ // Add menus
+ NSMenu *menu;
+ NSMenuItem *menuItem;
+
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+
+ // Application menu
+ menu = [[NSMenu alloc] initWithTitle:@""];
+ [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU
+ [menu addItem:[NSMenuItem separatorItem]]; //Separator
+ [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
+ menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+ [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
+ [menu addItem:[NSMenuItem separatorItem]]; //Separator
+ [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"];
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+ [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+)
+
+ // View menu
+ menu = [[NSMenu alloc] initWithTitle:@"View"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ // Window menu
+ menu = [[NSMenu alloc] initWithTitle:@"Window"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+ [NSApp setWindowsMenu:menu];
+
+ // Help menu
+ menu = [[NSMenu alloc] initWithTitle:@"Help"];
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help
+ [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help
+ menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+ [menuItem setSubmenu:menu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ // Create an Application controller
+ QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
+ [NSApp setDelegate:appController];
+
+ // Start the main event loop
+ [NSApp run];
+
+ [appController release];
+ [pool release];
+
+ return 0;
+}
+
+
+
+#pragma mark qemu
+static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
+
+ NSRect rect;
+ if ([cocoaView cdx] == 1.0) {
+ rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
+ } else {
+ rect = NSMakeRect(
+ x * [cocoaView cdx],
+ ([cocoaView gscreen].height - y - h) * [cocoaView cdy],
+ w * [cocoaView cdx],
+ h * [cocoaView cdy]);
+ }
+ [cocoaView setNeedsDisplayInRect:rect];
+}
+
+static void cocoa_resize(DisplayState *ds)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_resize\n");
+
+ [cocoaView resizeContentToWidth:(int)(ds_get_width(ds)) height:(int)(ds_get_height(ds)) displayState:ds];
+}
+
+static void cocoa_refresh(DisplayState *ds)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
+
+ if (kbd_mouse_is_absolute()) {
+ if (![cocoaView isAbsoluteEnabled]) {
+ if ([cocoaView isMouseGrabed]) {
+ [cocoaView ungrabMouse];
+ }
+ }
+ [cocoaView setAbsoluteEnabled:YES];
+ }
+
+ NSDate *distantPast;
+ NSEvent *event;
+ distantPast = [NSDate distantPast];
+ do {
+ event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
+ inMode: NSDefaultRunLoopMode dequeue:YES];
+ if (event != nil) {
+ [cocoaView handleEvent:event];
+ }
+ } while(event != nil);
+ vga_hw_update();
+}
+
+static void cocoa_cleanup(void)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n");
+ qemu_free(dcl);
+}
+
+void cocoa_display_init(DisplayState *ds, int full_screen)
+{
+ COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+
+ // register vga output callbacks
+ dcl->dpy_update = cocoa_update;
+ dcl->dpy_resize = cocoa_resize;
+ dcl->dpy_refresh = cocoa_refresh;
+
+ register_displaychangelistener(ds, dcl);
+
+ // register cleanup function
+ atexit(cocoa_cleanup);
+}
diff --git a/ui/curses.c b/ui/curses.c
new file mode 100644
index 0000000000..ed3165e45e
--- /dev/null
+++ b/ui/curses.c
@@ -0,0 +1,365 @@
+/*
+ * QEMU curses/ncurses display driver
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 <curses.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#ifdef __OpenBSD__
+#define resize_term resizeterm
+#endif
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+static console_ch_t screen[160 * 100];
+static WINDOW *screenpad = NULL;
+static int width, height, gwidth, gheight, invalidate;
+static int px, py, sminx, sminy, smaxx, smaxy;
+
+static void curses_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ chtype *line;
+
+ line = ((chtype *) screen) + y * width;
+ for (h += y; y < h; y ++, line += width)
+ mvwaddchnstr(screenpad, y, 0, line, width);
+
+ pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
+ refresh();
+}
+
+static void curses_calc_pad(void)
+{
+ if (is_fixedsize_console()) {
+ width = gwidth;
+ height = gheight;
+ } else {
+ width = COLS;
+ height = LINES;
+ }
+
+ if (screenpad)
+ delwin(screenpad);
+
+ clear();
+ refresh();
+
+ screenpad = newpad(height, width);
+
+ if (width > COLS) {
+ px = (width - COLS) / 2;
+ sminx = 0;
+ smaxx = COLS;
+ } else {
+ px = 0;
+ sminx = (COLS - width) / 2;
+ smaxx = sminx + width;
+ }
+
+ if (height > LINES) {
+ py = (height - LINES) / 2;
+ sminy = 0;
+ smaxy = LINES;
+ } else {
+ py = 0;
+ sminy = (LINES - height) / 2;
+ smaxy = sminy + height;
+ }
+}
+
+static void curses_resize(DisplayState *ds)
+{
+ if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight)
+ return;
+
+ gwidth = ds_get_width(ds);
+ gheight = ds_get_height(ds);
+
+ curses_calc_pad();
+ ds->surface->width = width * FONT_WIDTH;
+ ds->surface->height = height * FONT_HEIGHT;
+}
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+static void curses_winch_handler(int signum)
+{
+ struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel; /* unused */
+ unsigned short ws_ypixel; /* unused */
+ } ws;
+
+ /* terminal size changed */
+ if (ioctl(1, TIOCGWINSZ, &ws) == -1)
+ return;
+
+ resize_term(ws.ws_row, ws.ws_col);
+ curses_calc_pad();
+ invalidate = 1;
+
+ /* some systems require this */
+ signal(SIGWINCH, curses_winch_handler);
+}
+#endif
+#endif
+
+static void curses_cursor_position(DisplayState *ds, int x, int y)
+{
+ if (x >= 0) {
+ x = sminx + x - px;
+ y = sminy + y - py;
+
+ if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
+ move(y, x);
+ curs_set(1);
+ /* it seems that curs_set(1) must always be called before
+ * curs_set(2) for the latter to have effect */
+ if (!is_graphic_console())
+ curs_set(2);
+ return;
+ }
+ }
+
+ curs_set(0);
+}
+
+/* generic keyboard conversion */
+
+#include "curses_keys.h"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static void curses_refresh(DisplayState *ds)
+{
+ int chr, nextchr, keysym, keycode, keycode_alt;
+
+ if (invalidate) {
+ clear();
+ refresh();
+ curses_calc_pad();
+ ds->surface->width = FONT_WIDTH * width;
+ ds->surface->height = FONT_HEIGHT * height;
+ vga_hw_invalidate();
+ invalidate = 0;
+ }
+
+ vga_hw_text_update(screen);
+
+ nextchr = ERR;
+ while (1) {
+ /* while there are any pending key strokes to process */
+ if (nextchr == ERR)
+ chr = getch();
+ else {
+ chr = nextchr;
+ nextchr = ERR;
+ }
+
+ if (chr == ERR)
+ break;
+
+#ifdef KEY_RESIZE
+ /* this shouldn't occur when we use a custom SIGWINCH handler */
+ if (chr == KEY_RESIZE) {
+ clear();
+ refresh();
+ curses_calc_pad();
+ curses_update(ds, 0, 0, width, height);
+ ds->surface->width = FONT_WIDTH * width;
+ ds->surface->height = FONT_HEIGHT * height;
+ continue;
+ }
+#endif
+
+ keycode = curses2keycode[chr];
+ keycode_alt = 0;
+
+ /* alt key */
+ if (keycode == 1) {
+ nextchr = getch();
+
+ if (nextchr != ERR) {
+ chr = nextchr;
+ keycode_alt = ALT;
+ keycode = curses2keycode[nextchr];
+ nextchr = ERR;
+
+ if (keycode != -1) {
+ keycode |= ALT;
+
+ /* process keys reserved for qemu */
+ if (keycode >= QEMU_KEY_CONSOLE0 &&
+ keycode < QEMU_KEY_CONSOLE0 + 9) {
+ erase();
+ wnoutrefresh(stdscr);
+ console_select(keycode - QEMU_KEY_CONSOLE0);
+
+ invalidate = 1;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (kbd_layout) {
+ keysym = -1;
+ if (chr < CURSES_KEYS)
+ keysym = curses2keysym[chr];
+
+ if (keysym == -1) {
+ if (chr < ' ')
+ keysym = (chr + '@' - 'A' + 'a') | KEYSYM_CNTRL;
+ else
+ keysym = chr;
+ }
+
+ keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK);
+ if (keycode == 0)
+ continue;
+
+ keycode |= (keysym & ~KEYSYM_MASK) >> 16;
+ keycode |= keycode_alt;
+ }
+
+ if (keycode == -1)
+ continue;
+
+ if (is_graphic_console()) {
+ /* since terminals don't know about key press and release
+ * events, we need to emit both for each key received */
+ if (keycode & SHIFT)
+ kbd_put_keycode(SHIFT_CODE);
+ if (keycode & CNTRL)
+ kbd_put_keycode(CNTRL_CODE);
+ if (keycode & ALT)
+ kbd_put_keycode(ALT_CODE);
+ if (keycode & ALTGR) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(ALT_CODE);
+ }
+ if (keycode & GREY)
+ kbd_put_keycode(GREY_CODE);
+ kbd_put_keycode(keycode & KEY_MASK);
+ if (keycode & GREY)
+ kbd_put_keycode(GREY_CODE);
+ kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+ if (keycode & ALTGR) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+ }
+ if (keycode & ALT)
+ kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+ if (keycode & CNTRL)
+ kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
+ if (keycode & SHIFT)
+ kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
+ } else {
+ keysym = curses2qemu[chr];
+ if (keysym == -1)
+ keysym = chr;
+
+ kbd_put_keysym(keysym);
+ }
+ }
+}
+
+static void curses_atexit(void)
+{
+ endwin();
+}
+
+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,
+ };
+
+ /* input as raw as possible, let everything be interpreted
+ * by the guest system */
+ initscr(); noecho(); intrflush(stdscr, FALSE);
+ nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
+ start_color(); raw(); scrollok(stdscr, FALSE);
+
+ for (i = 0; i < 64; i ++)
+ init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
+}
+
+static void curses_keyboard_setup(void)
+{
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+}
+
+void curses_display_init(DisplayState *ds, int full_screen)
+{
+ DisplayChangeListener *dcl;
+#ifndef _WIN32
+ if (!isatty(1)) {
+ fprintf(stderr, "We need a terminal output\n");
+ exit(1);
+ }
+#endif
+
+ curses_setup();
+ curses_keyboard_setup();
+ atexit(curses_atexit);
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+ /* some curses implementations provide a handler, but we
+ * want to be sure this is handled regardless of the library */
+ signal(SIGWINCH, curses_winch_handler);
+#endif
+#endif
+
+ dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = curses_update;
+ dcl->dpy_resize = curses_resize;
+ dcl->dpy_refresh = curses_refresh;
+ dcl->dpy_text_cursor = curses_cursor_position;
+ register_displaychangelistener(ds, dcl);
+ qemu_free_displaysurface(ds);
+ ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen);
+
+ invalidate = 1;
+}
diff --git a/ui/curses_keys.h b/ui/curses_keys.h
new file mode 100644
index 0000000000..1decd1119d
--- /dev/null
+++ b/ui/curses_keys.h
@@ -0,0 +1,508 @@
+/*
+ * Keycode and keysyms conversion tables for curses
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 <curses.h>
+#include "keymaps.h"
+
+
+#define KEY_RELEASE 0x80
+#define KEY_MASK 0x7f
+#define GREY_CODE 0xe0
+#define GREY SCANCODE_GREY
+#define SHIFT_CODE 0x2a
+#define SHIFT SCANCODE_SHIFT
+#define CNTRL_CODE 0x1d
+#define CNTRL SCANCODE_CTRL
+#define ALT_CODE 0x38
+#define ALT SCANCODE_ALT
+#define ALTGR SCANCODE_ALTGR
+
+#define KEYSYM_MASK 0x0ffffff
+#define KEYSYM_SHIFT (SCANCODE_SHIFT << 16)
+#define KEYSYM_CNTRL (SCANCODE_CTRL << 16)
+#define KEYSYM_ALT (SCANCODE_ALT << 16)
+#define KEYSYM_ALTGR (SCANCODE_ALTGR << 16)
+
+/* curses won't detect a Control + Alt + 1, so use Alt + 1 */
+#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */
+
+#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in <curses.h> */
+
+static const int curses2keysym[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ [0x7f] = KEY_BACKSPACE,
+ ['\r'] = KEY_ENTER,
+ ['\n'] = KEY_ENTER,
+ [KEY_BTAB] = '\t' | KEYSYM_SHIFT,
+};
+
+static const int curses2keycode[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ [0x01b] = 1, /* Escape */
+ ['1'] = 2,
+ ['2'] = 3,
+ ['3'] = 4,
+ ['4'] = 5,
+ ['5'] = 6,
+ ['6'] = 7,
+ ['7'] = 8,
+ ['8'] = 9,
+ ['9'] = 10,
+ ['0'] = 11,
+ ['-'] = 12,
+ ['='] = 13,
+ [0x07f] = 14, /* Backspace */
+ [KEY_BACKSPACE] = 14, /* Backspace */
+
+ ['\t'] = 15, /* Tab */
+ ['q'] = 16,
+ ['w'] = 17,
+ ['e'] = 18,
+ ['r'] = 19,
+ ['t'] = 20,
+ ['y'] = 21,
+ ['u'] = 22,
+ ['i'] = 23,
+ ['o'] = 24,
+ ['p'] = 25,
+ ['['] = 26,
+ [']'] = 27,
+ ['\n'] = 28, /* Return */
+ ['\r'] = 28, /* Return */
+ [KEY_ENTER] = 28, /* Return */
+
+ ['a'] = 30,
+ ['s'] = 31,
+ ['d'] = 32,
+ ['f'] = 33,
+ ['g'] = 34,
+ ['h'] = 35,
+ ['j'] = 36,
+ ['k'] = 37,
+ ['l'] = 38,
+ [';'] = 39,
+ ['\''] = 40, /* Single quote */
+ ['`'] = 41,
+ ['\\'] = 43, /* Backslash */
+
+ ['z'] = 44,
+ ['x'] = 45,
+ ['c'] = 46,
+ ['v'] = 47,
+ ['b'] = 48,
+ ['n'] = 49,
+ ['m'] = 50,
+ [','] = 51,
+ ['.'] = 52,
+ ['/'] = 53,
+
+ [' '] = 57,
+
+ [KEY_F(1)] = 59, /* Function Key 1 */
+ [KEY_F(2)] = 60, /* Function Key 2 */
+ [KEY_F(3)] = 61, /* Function Key 3 */
+ [KEY_F(4)] = 62, /* Function Key 4 */
+ [KEY_F(5)] = 63, /* Function Key 5 */
+ [KEY_F(6)] = 64, /* Function Key 6 */
+ [KEY_F(7)] = 65, /* Function Key 7 */
+ [KEY_F(8)] = 66, /* Function Key 8 */
+ [KEY_F(9)] = 67, /* Function Key 9 */
+ [KEY_F(10)] = 68, /* Function Key 10 */
+ [KEY_F(11)] = 87, /* Function Key 11 */
+ [KEY_F(12)] = 88, /* Function Key 12 */
+
+ [KEY_HOME] = 71 | GREY, /* Home */
+ [KEY_UP] = 72 | GREY, /* Up Arrow */
+ [KEY_PPAGE] = 73 | GREY, /* Page Up */
+ [KEY_LEFT] = 75 | GREY, /* Left Arrow */
+ [KEY_RIGHT] = 77 | GREY, /* Right Arrow */
+ [KEY_END] = 79 | GREY, /* End */
+ [KEY_DOWN] = 80 | GREY, /* Down Arrow */
+ [KEY_NPAGE] = 81 | GREY, /* Page Down */
+ [KEY_IC] = 82 | GREY, /* Insert */
+ [KEY_DC] = 83 | GREY, /* Delete */
+
+ ['!'] = 2 | SHIFT,
+ ['@'] = 3 | SHIFT,
+ ['#'] = 4 | SHIFT,
+ ['$'] = 5 | SHIFT,
+ ['%'] = 6 | SHIFT,
+ ['^'] = 7 | SHIFT,
+ ['&'] = 8 | SHIFT,
+ ['*'] = 9 | SHIFT,
+ ['('] = 10 | SHIFT,
+ [')'] = 11 | SHIFT,
+ ['_'] = 12 | SHIFT,
+ ['+'] = 13 | SHIFT,
+
+ [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */
+ ['Q'] = 16 | SHIFT,
+ ['W'] = 17 | SHIFT,
+ ['E'] = 18 | SHIFT,
+ ['R'] = 19 | SHIFT,
+ ['T'] = 20 | SHIFT,
+ ['Y'] = 21 | SHIFT,
+ ['U'] = 22 | SHIFT,
+ ['I'] = 23 | SHIFT,
+ ['O'] = 24 | SHIFT,
+ ['P'] = 25 | SHIFT,
+ ['{'] = 26 | SHIFT,
+ ['}'] = 27 | SHIFT,
+
+ ['A'] = 30 | SHIFT,
+ ['S'] = 31 | SHIFT,
+ ['D'] = 32 | SHIFT,
+ ['F'] = 33 | SHIFT,
+ ['G'] = 34 | SHIFT,
+ ['H'] = 35 | SHIFT,
+ ['J'] = 36 | SHIFT,
+ ['K'] = 37 | SHIFT,
+ ['L'] = 38 | SHIFT,
+ [':'] = 39 | SHIFT,
+ ['"'] = 40 | SHIFT,
+ ['~'] = 41 | SHIFT,
+ ['|'] = 43 | SHIFT,
+
+ ['Z'] = 44 | SHIFT,
+ ['X'] = 45 | SHIFT,
+ ['C'] = 46 | SHIFT,
+ ['V'] = 47 | SHIFT,
+ ['B'] = 48 | SHIFT,
+ ['N'] = 49 | SHIFT,
+ ['M'] = 50 | SHIFT,
+ ['<'] = 51 | SHIFT,
+ ['>'] = 52 | SHIFT,
+ ['?'] = 53 | SHIFT,
+
+ [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */
+ [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */
+ [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */
+ [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */
+ [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */
+ [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */
+ [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */
+ [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */
+ [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */
+ [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */
+ [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */
+ [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */
+
+ ['Q' - '@'] = 16 | CNTRL, /* Control + q */
+ ['W' - '@'] = 17 | CNTRL, /* Control + w */
+ ['E' - '@'] = 18 | CNTRL, /* Control + e */
+ ['R' - '@'] = 19 | CNTRL, /* Control + r */
+ ['T' - '@'] = 20 | CNTRL, /* Control + t */
+ ['Y' - '@'] = 21 | CNTRL, /* Control + y */
+ ['U' - '@'] = 22 | CNTRL, /* Control + u */
+ /* Control + i collides with Tab */
+ ['O' - '@'] = 24 | CNTRL, /* Control + o */
+ ['P' - '@'] = 25 | CNTRL, /* Control + p */
+
+ ['A' - '@'] = 30 | CNTRL, /* Control + a */
+ ['S' - '@'] = 31 | CNTRL, /* Control + s */
+ ['D' - '@'] = 32 | CNTRL, /* Control + d */
+ ['F' - '@'] = 33 | CNTRL, /* Control + f */
+ ['G' - '@'] = 34 | CNTRL, /* Control + g */
+ ['H' - '@'] = 35 | CNTRL, /* Control + h */
+ /* Control + j collides with Return */
+ ['K' - '@'] = 37 | CNTRL, /* Control + k */
+ ['L' - '@'] = 38 | CNTRL, /* Control + l */
+
+ ['Z' - '@'] = 44 | CNTRL, /* Control + z */
+ ['X' - '@'] = 45 | CNTRL, /* Control + x */
+ ['C' - '@'] = 46 | CNTRL, /* Control + c */
+ ['V' - '@'] = 47 | CNTRL, /* Control + v */
+ ['B' - '@'] = 48 | CNTRL, /* Control + b */
+ ['N' - '@'] = 49 | CNTRL, /* Control + n */
+ /* Control + m collides with the keycode for Enter */
+
+};
+
+static const int curses2qemu[CURSES_KEYS] = {
+ [0 ... (CURSES_KEYS - 1)] = -1,
+
+ ['\n'] = '\n',
+ ['\r'] = '\n',
+
+ [0x07f] = QEMU_KEY_BACKSPACE,
+
+ [KEY_DOWN] = QEMU_KEY_DOWN,
+ [KEY_UP] = QEMU_KEY_UP,
+ [KEY_LEFT] = QEMU_KEY_LEFT,
+ [KEY_RIGHT] = QEMU_KEY_RIGHT,
+ [KEY_HOME] = QEMU_KEY_HOME,
+ [KEY_BACKSPACE] = QEMU_KEY_BACKSPACE,
+
+ [KEY_DC] = QEMU_KEY_DELETE,
+ [KEY_NPAGE] = QEMU_KEY_PAGEDOWN,
+ [KEY_PPAGE] = QEMU_KEY_PAGEUP,
+ [KEY_ENTER] = '\n',
+ [KEY_END] = QEMU_KEY_END,
+
+};
+
+static const name2keysym_t name2keysym[] = {
+ /* Plain ASCII */
+ { "space", 0x020 },
+ { "exclam", 0x021 },
+ { "quotedbl", 0x022 },
+ { "numbersign", 0x023 },
+ { "dollar", 0x024 },
+ { "percent", 0x025 },
+ { "ampersand", 0x026 },
+ { "apostrophe", 0x027 },
+ { "parenleft", 0x028 },
+ { "parenright", 0x029 },
+ { "asterisk", 0x02a },
+ { "plus", 0x02b },
+ { "comma", 0x02c },
+ { "minus", 0x02d },
+ { "period", 0x02e },
+ { "slash", 0x02f },
+ { "0", 0x030 },
+ { "1", 0x031 },
+ { "2", 0x032 },
+ { "3", 0x033 },
+ { "4", 0x034 },
+ { "5", 0x035 },
+ { "6", 0x036 },
+ { "7", 0x037 },
+ { "8", 0x038 },
+ { "9", 0x039 },
+ { "colon", 0x03a },
+ { "semicolon", 0x03b },
+ { "less", 0x03c },
+ { "equal", 0x03d },
+ { "greater", 0x03e },
+ { "question", 0x03f },
+ { "at", 0x040 },
+ { "A", 0x041 },
+ { "B", 0x042 },
+ { "C", 0x043 },
+ { "D", 0x044 },
+ { "E", 0x045 },
+ { "F", 0x046 },
+ { "G", 0x047 },
+ { "H", 0x048 },
+ { "I", 0x049 },
+ { "J", 0x04a },
+ { "K", 0x04b },
+ { "L", 0x04c },
+ { "M", 0x04d },
+ { "N", 0x04e },
+ { "O", 0x04f },
+ { "P", 0x050 },
+ { "Q", 0x051 },
+ { "R", 0x052 },
+ { "S", 0x053 },
+ { "T", 0x054 },
+ { "U", 0x055 },
+ { "V", 0x056 },
+ { "W", 0x057 },
+ { "X", 0x058 },
+ { "Y", 0x059 },
+ { "Z", 0x05a },
+ { "bracketleft", 0x05b },
+ { "backslash", 0x05c },
+ { "bracketright", 0x05d },
+ { "asciicircum", 0x05e },
+ { "underscore", 0x05f },
+ { "grave", 0x060 },
+ { "a", 0x061 },
+ { "b", 0x062 },
+ { "c", 0x063 },
+ { "d", 0x064 },
+ { "e", 0x065 },
+ { "f", 0x066 },
+ { "g", 0x067 },
+ { "h", 0x068 },
+ { "i", 0x069 },
+ { "j", 0x06a },
+ { "k", 0x06b },
+ { "l", 0x06c },
+ { "m", 0x06d },
+ { "n", 0x06e },
+ { "o", 0x06f },
+ { "p", 0x070 },
+ { "q", 0x071 },
+ { "r", 0x072 },
+ { "s", 0x073 },
+ { "t", 0x074 },
+ { "u", 0x075 },
+ { "v", 0x076 },
+ { "w", 0x077 },
+ { "x", 0x078 },
+ { "y", 0x079 },
+ { "z", 0x07a },
+ { "braceleft", 0x07b },
+ { "bar", 0x07c },
+ { "braceright", 0x07d },
+ { "asciitilde", 0x07e },
+
+ /* Latin-1 extensions */
+ { "nobreakspace", 0x0a0 },
+ { "exclamdown", 0x0a1 },
+ { "cent", 0x0a2 },
+ { "sterling", 0x0a3 },
+ { "currency", 0x0a4 },
+ { "yen", 0x0a5 },
+ { "brokenbar", 0x0a6 },
+ { "section", 0x0a7 },
+ { "diaeresis", 0x0a8 },
+ { "copyright", 0x0a9 },
+ { "ordfeminine", 0x0aa },
+ { "guillemotleft", 0x0ab },
+ { "notsign", 0x0ac },
+ { "hyphen", 0x0ad },
+ { "registered", 0x0ae },
+ { "macron", 0x0af },
+ { "degree", 0x0b0 },
+ { "plusminus", 0x0b1 },
+ { "twosuperior", 0x0b2 },
+ { "threesuperior", 0x0b3 },
+ { "acute", 0x0b4 },
+ { "mu", 0x0b5 },
+ { "paragraph", 0x0b6 },
+ { "periodcentered", 0x0b7 },
+ { "cedilla", 0x0b8 },
+ { "onesuperior", 0x0b9 },
+ { "masculine", 0x0ba },
+ { "guillemotright", 0x0bb },
+ { "onequarter", 0x0bc },
+ { "onehalf", 0x0bd },
+ { "threequarters", 0x0be },
+ { "questiondown", 0x0bf },
+ { "Agrave", 0x0c0 },
+ { "Aacute", 0x0c1 },
+ { "Acircumflex", 0x0c2 },
+ { "Atilde", 0x0c3 },
+ { "Adiaeresis", 0x0c4 },
+ { "Aring", 0x0c5 },
+ { "AE", 0x0c6 },
+ { "Ccedilla", 0x0c7 },
+ { "Egrave", 0x0c8 },
+ { "Eacute", 0x0c9 },
+ { "Ecircumflex", 0x0ca },
+ { "Ediaeresis", 0x0cb },
+ { "Igrave", 0x0cc },
+ { "Iacute", 0x0cd },
+ { "Icircumflex", 0x0ce },
+ { "Idiaeresis", 0x0cf },
+ { "ETH", 0x0d0 },
+ { "Eth", 0x0d0 },
+ { "Ntilde", 0x0d1 },
+ { "Ograve", 0x0d2 },
+ { "Oacute", 0x0d3 },
+ { "Ocircumflex", 0x0d4 },
+ { "Otilde", 0x0d5 },
+ { "Odiaeresis", 0x0d6 },
+ { "multiply", 0x0d7 },
+ { "Ooblique", 0x0d8 },
+ { "Oslash", 0x0d8 },
+ { "Ugrave", 0x0d9 },
+ { "Uacute", 0x0da },
+ { "Ucircumflex", 0x0db },
+ { "Udiaeresis", 0x0dc },
+ { "Yacute", 0x0dd },
+ { "THORN", 0x0de },
+ { "Thorn", 0x0de },
+ { "ssharp", 0x0df },
+ { "agrave", 0x0e0 },
+ { "aacute", 0x0e1 },
+ { "acircumflex", 0x0e2 },
+ { "atilde", 0x0e3 },
+ { "adiaeresis", 0x0e4 },
+ { "aring", 0x0e5 },
+ { "ae", 0x0e6 },
+ { "ccedilla", 0x0e7 },
+ { "egrave", 0x0e8 },
+ { "eacute", 0x0e9 },
+ { "ecircumflex", 0x0ea },
+ { "ediaeresis", 0x0eb },
+ { "igrave", 0x0ec },
+ { "iacute", 0x0ed },
+ { "icircumflex", 0x0ee },
+ { "idiaeresis", 0x0ef },
+ { "eth", 0x0f0 },
+ { "ntilde", 0x0f1 },
+ { "ograve", 0x0f2 },
+ { "oacute", 0x0f3 },
+ { "ocircumflex", 0x0f4 },
+ { "otilde", 0x0f5 },
+ { "odiaeresis", 0x0f6 },
+ { "division", 0x0f7 },
+ { "oslash", 0x0f8 },
+ { "ooblique", 0x0f8 },
+ { "ugrave", 0x0f9 },
+ { "uacute", 0x0fa },
+ { "ucircumflex", 0x0fb },
+ { "udiaeresis", 0x0fc },
+ { "yacute", 0x0fd },
+ { "thorn", 0x0fe },
+ { "ydiaeresis", 0x0ff },
+
+ /* Special keys */
+ { "BackSpace", KEY_BACKSPACE },
+ { "Tab", '\t' },
+ { "Return", KEY_ENTER },
+ { "Right", KEY_RIGHT },
+ { "Left", KEY_LEFT },
+ { "Up", KEY_UP },
+ { "Down", KEY_DOWN },
+ { "Page_Down", KEY_NPAGE },
+ { "Page_Up", KEY_PPAGE },
+ { "Insert", KEY_IC },
+ { "Delete", KEY_DC },
+ { "Home", KEY_HOME },
+ { "End", KEY_END },
+ { "F1", KEY_F(1) },
+ { "F2", KEY_F(2) },
+ { "F3", KEY_F(3) },
+ { "F4", KEY_F(4) },
+ { "F5", KEY_F(5) },
+ { "F6", KEY_F(6) },
+ { "F7", KEY_F(7) },
+ { "F8", KEY_F(8) },
+ { "F9", KEY_F(9) },
+ { "F10", KEY_F(10) },
+ { "F11", KEY_F(11) },
+ { "F12", KEY_F(12) },
+ { "F13", KEY_F(13) },
+ { "F14", KEY_F(14) },
+ { "F15", KEY_F(15) },
+ { "F16", KEY_F(16) },
+ { "F17", KEY_F(17) },
+ { "F18", KEY_F(18) },
+ { "F19", KEY_F(19) },
+ { "F20", KEY_F(20) },
+ { "F21", KEY_F(21) },
+ { "F22", KEY_F(22) },
+ { "F23", KEY_F(23) },
+ { "F24", KEY_F(24) },
+ { "Escape", 27 },
+
+ { NULL, 0 },
+};
diff --git a/ui/d3des.c b/ui/d3des.c
new file mode 100644
index 0000000000..60c840ed53
--- /dev/null
+++ b/ui/d3des.c
@@ -0,0 +1,424 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC. Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static const unsigned short bytebit[8] = {
+ 01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static const unsigned long bigbyte[24] = {
+ 0x800000L, 0x400000L, 0x200000L, 0x100000L,
+ 0x80000L, 0x40000L, 0x20000L, 0x10000L,
+ 0x8000L, 0x4000L, 0x2000L, 0x1000L,
+ 0x800L, 0x400L, 0x200L, 0x100L,
+ 0x80L, 0x40L, 0x20L, 0x10L,
+ 0x8L, 0x4L, 0x2L, 0x1L };
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static const unsigned char pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
+
+static const unsigned char totrot[16] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static const unsigned char pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+/* Thanks to James Gillogly & Phil Karn! */
+void deskey(unsigned char *key, int edf)
+{
+ register int i, j, l, m, n;
+ unsigned char pc1m[56], pcr[56];
+ unsigned long kn[32];
+
+ for ( j = 0; j < 56; j++ ) {
+ l = pc1[j];
+ m = l & 07;
+ pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+ }
+ for( i = 0; i < 16; i++ ) {
+ if( edf == DE1 ) m = (15 - i) << 1;
+ else m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for( j = 0; j < 28; j++ ) {
+ l = j + totrot[i];
+ if( l < 28 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 28; j < 56; j++ ) {
+ l = j + totrot[i];
+ if( l < 56 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 0; j < 24; j++ ) {
+ if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+ if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+ }
+ }
+ cookey(kn);
+ return;
+ }
+
+static void cookey(register unsigned long *raw1)
+{
+ register unsigned long *cook, *raw0;
+ unsigned long dough[32];
+ register int i;
+
+ cook = dough;
+ for( i = 0; i < 16; i++, raw1++ ) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+ usekey(dough);
+ return;
+ }
+
+void cpkey(register unsigned long *into)
+{
+ register unsigned long *from, *endp;
+
+ from = KnL, endp = &KnL[32];
+ while( from < endp ) *into++ = *from++;
+ return;
+ }
+
+void usekey(register unsigned long *from)
+{
+ register unsigned long *to, *endp;
+
+ to = KnL, endp = &KnL[32];
+ while( to < endp ) *to++ = *from++;
+ return;
+ }
+
+void des(unsigned char *inblock, unsigned char *outblock)
+{
+ unsigned long work[2];
+
+ scrunch(inblock, work);
+ desfunc(work, KnL);
+ unscrun(work, outblock);
+ return;
+ }
+
+static void scrunch(register unsigned char *outof, register unsigned long *into)
+{
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into++ |= (*outof++ & 0xffL);
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into |= (*outof & 0xffL);
+ return;
+ }
+
+static void unscrun(register unsigned long *outof, register unsigned char *into)
+{
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into++ = (unsigned char)(*outof++ & 0xffL);
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into = (unsigned char)(*outof & 0xffL);
+ return;
+ }
+
+static const unsigned long SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static const unsigned long SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static const unsigned long SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static const unsigned long SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static const unsigned long SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static const unsigned long SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static const unsigned long SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static const unsigned long SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(register unsigned long *block, register unsigned long *keys)
+{
+ register unsigned long fval, work, right, leftt;
+ register int round;
+
+ leftt = block[0];
+ right = block[1];
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ leftt ^= fval;
+ work = (leftt << 28) | (leftt >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = (leftt << 31) | (leftt >> 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+ *block++ = right;
+ *block = leftt;
+ return;
+ }
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/ui/d3des.h b/ui/d3des.h
new file mode 100644
index 0000000000..ea3da44ce9
--- /dev/null
+++ b/ui/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* d3des.h -
+ *
+ * Headers and defines for d3des.c
+ * Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ * (GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0 0 /* MODE == encrypt */
+#define DE1 1 /* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/* hexkey[8] MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/* cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/* cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/* from[8] to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'. They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/ui/keymaps.c b/ui/keymaps.c
new file mode 100644
index 0000000000..78c7ea375c
--- /dev/null
+++ b/ui/keymaps.c
@@ -0,0 +1,210 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * 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 "keymaps.h"
+#include "sysemu.h"
+
+static int get_keysym(const name2keysym_t *table,
+ const char *name)
+{
+ const name2keysym_t *p;
+ for(p = table; p->name != NULL; p++) {
+ if (!strcmp(p->name, name))
+ return p->keysym;
+ }
+ return 0;
+}
+
+
+static void add_to_key_range(struct key_range **krp, int code) {
+ struct key_range *kr;
+ for (kr = *krp; kr; kr = kr->next) {
+ if (code >= kr->start && code <= kr->end)
+ break;
+ if (code == kr->start - 1) {
+ kr->start--;
+ break;
+ }
+ if (code == kr->end + 1) {
+ kr->end++;
+ break;
+ }
+ }
+ if (kr == NULL) {
+ kr = qemu_mallocz(sizeof(*kr));
+ kr->start = kr->end = code;
+ kr->next = *krp;
+ *krp = kr;
+ }
+}
+
+static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) {
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
+ k->keysym2keycode[keysym] = keycode;
+ } else {
+ if (k->extra_count >= MAX_EXTRA_COUNT) {
+ fprintf(stderr,
+ "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n",
+ line, keysym);
+ } else {
+#if 0
+ fprintf(stderr, "Setting %d: %d,%d\n",
+ k->extra_count, keysym, keycode);
+#endif
+ k->keysym2keycode_extra[k->extra_count].
+ keysym = keysym;
+ k->keysym2keycode_extra[k->extra_count].
+ keycode = keycode;
+ k->extra_count++;
+ }
+ }
+}
+
+static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table,
+ const char *language,
+ kbd_layout_t * k)
+{
+ FILE *f;
+ char * filename;
+ char line[1024];
+ int len;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language);
+
+ if (!k)
+ k = qemu_mallocz(sizeof(kbd_layout_t));
+ if (!(filename && (f = fopen(filename, "r")))) {
+ fprintf(stderr,
+ "Could not read keymap file: '%s'\n", language);
+ return NULL;
+ }
+ qemu_free(filename);
+ for(;;) {
+ if (fgets(line, 1024, f) == NULL)
+ break;
+ len = strlen(line);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+ if (!strncmp(line, "map ", 4))
+ continue;
+ if (!strncmp(line, "include ", 8)) {
+ parse_keyboard_layout(table, line + 8, k);
+ } else {
+ char *end_of_keysym = line;
+ while (*end_of_keysym != 0 && *end_of_keysym != ' ')
+ end_of_keysym++;
+ if (*end_of_keysym) {
+ int keysym;
+ *end_of_keysym = 0;
+ keysym = get_keysym(table, line);
+ if (keysym == 0) {
+ // fprintf(stderr, "Warning: unknown keysym %s\n", line);
+ } else {
+ const char *rest = end_of_keysym + 1;
+ char *rest2;
+ int keycode = strtol(rest, &rest2, 0);
+
+ if (rest && strstr(rest, "numlock")) {
+ add_to_key_range(&k->keypad_range, keycode);
+ add_to_key_range(&k->numlock_range, keysym);
+ //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode);
+ }
+
+ if (rest && strstr(rest, "shift"))
+ keycode |= SCANCODE_SHIFT;
+ if (rest && strstr(rest, "altgr"))
+ keycode |= SCANCODE_ALTGR;
+ if (rest && strstr(rest, "ctrl"))
+ keycode |= SCANCODE_CTRL;
+
+ add_keysym(line, keysym, keycode, k);
+
+ if (rest && strstr(rest, "addupper")) {
+ char *c;
+ for (c = line; *c; c++)
+ *c = toupper(*c);
+ keysym = get_keysym(table, line);
+ if (keysym)
+ add_keysym(line, keysym, keycode | SCANCODE_SHIFT, k);
+ }
+ }
+ }
+ }
+ }
+ fclose(f);
+ return k;
+}
+
+
+void *init_keyboard_layout(const name2keysym_t *table, const char *language)
+{
+ return parse_keyboard_layout(table, language, NULL);
+}
+
+
+int keysym2scancode(void *kbd_layout, int keysym)
+{
+ kbd_layout_t *k = kbd_layout;
+ if (keysym < MAX_NORMAL_KEYCODE) {
+ if (k->keysym2keycode[keysym] == 0)
+ fprintf(stderr, "Warning: no scancode found for keysym %d\n",
+ keysym);
+ return k->keysym2keycode[keysym];
+ } else {
+ int i;
+#ifdef XK_ISO_Left_Tab
+ if (keysym == XK_ISO_Left_Tab)
+ keysym = XK_Tab;
+#endif
+ for (i = 0; i < k->extra_count; i++)
+ if (k->keysym2keycode_extra[i].keysym == keysym)
+ return k->keysym2keycode_extra[i].keycode;
+ }
+ return 0;
+}
+
+int keycode_is_keypad(void *kbd_layout, int keycode)
+{
+ kbd_layout_t *k = kbd_layout;
+ struct key_range *kr;
+
+ for (kr = k->keypad_range; kr; kr = kr->next)
+ if (keycode >= kr->start && keycode <= kr->end)
+ return 1;
+ return 0;
+}
+
+int keysym_is_numlock(void *kbd_layout, int keysym)
+{
+ kbd_layout_t *k = kbd_layout;
+ struct key_range *kr;
+
+ for (kr = k->numlock_range; kr; kr = kr->next)
+ if (keysym >= kr->start && keysym <= kr->end)
+ return 1;
+ return 0;
+}
diff --git a/ui/keymaps.h b/ui/keymaps.h
new file mode 100644
index 0000000000..a7600d5751
--- /dev/null
+++ b/ui/keymaps.h
@@ -0,0 +1,77 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * 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_KEYMAPS_H__
+#define __QEMU_KEYMAPS_H__
+
+#include "qemu-common.h"
+
+typedef struct {
+ const char* name;
+ int keysym;
+} name2keysym_t;
+
+struct key_range {
+ int start;
+ int end;
+ struct key_range *next;
+};
+
+#define MAX_NORMAL_KEYCODE 512
+#define MAX_EXTRA_COUNT 256
+typedef struct {
+ uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+ struct {
+ int keysym;
+ uint16_t keycode;
+ } keysym2keycode_extra[MAX_EXTRA_COUNT];
+ int extra_count;
+ struct key_range *keypad_range;
+ struct key_range *numlock_range;
+} kbd_layout_t;
+
+/* scancode without modifiers */
+#define SCANCODE_KEYMASK 0xff
+/* scancode without grey or up bit */
+#define SCANCODE_KEYCODEMASK 0x7f
+
+/* "grey" keys will usually need a 0xe0 prefix */
+#define SCANCODE_GREY 0x80
+#define SCANCODE_EMUL0 0xE0
+/* "up" flag */
+#define SCANCODE_UP 0x80
+
+/* Additional modifiers to use if not catched another way. */
+#define SCANCODE_SHIFT 0x100
+#define SCANCODE_CTRL 0x200
+#define SCANCODE_ALT 0x400
+#define SCANCODE_ALTGR 0x800
+
+
+void *init_keyboard_layout(const name2keysym_t *table, const char *language);
+int keysym2scancode(void *kbd_layout, int keysym);
+int keycode_is_keypad(void *kbd_layout, int keycode);
+int keysym_is_numlock(void *kbd_layout, int keysym);
+
+#endif /* __QEMU_KEYMAPS_H__ */
diff --git a/ui/sdl.c b/ui/sdl.c
new file mode 100644
index 0000000000..0072680f4c
--- /dev/null
+++ b/ui/sdl.c
@@ -0,0 +1,879 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <SDL.h>
+#include <SDL_syswm.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#endif
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+#include "x_keymap.h"
+#include "sdl_zoom.h"
+
+static DisplayChangeListener *dcl;
+static SDL_Surface *real_screen;
+static SDL_Surface *guest_screen = NULL;
+static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
+static int last_vm_running;
+static int gui_saved_grab;
+static int gui_fullscreen;
+static int gui_noframe;
+static int gui_key_modifier_pressed;
+static int gui_keysym;
+static int gui_fullscreen_initial_grab;
+static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
+static uint8_t modifiers_state[256];
+static int width, height;
+static SDL_Cursor *sdl_cursor_normal;
+static SDL_Cursor *sdl_cursor_hidden;
+static int absolute_enabled = 0;
+static int guest_cursor = 0;
+static int guest_x, guest_y;
+static SDL_Cursor *guest_sprite = NULL;
+static uint8_t allocator;
+static SDL_PixelFormat host_format;
+static int scaling_active = 0;
+static Notifier mouse_mode_notifier;
+
+static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
+ SDL_Rect rec;
+ rec.x = x;
+ rec.y = y;
+ rec.w = w;
+ rec.h = h;
+
+ if (guest_screen) {
+ if (!scaling_active) {
+ SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
+ } else {
+ if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
+ fprintf(stderr, "Zoom blit failed\n");
+ exit(1);
+ }
+ }
+ }
+ SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
+}
+
+static void sdl_setdata(DisplayState *ds)
+{
+ SDL_Rect rec;
+ rec.x = 0;
+ rec.y = 0;
+ rec.w = real_screen->w;
+ rec.h = real_screen->h;
+
+ if (guest_screen != NULL) SDL_FreeSurface(guest_screen);
+
+ guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
+ ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
+ ds->surface->pf.rmask, ds->surface->pf.gmask,
+ ds->surface->pf.bmask, ds->surface->pf.amask);
+}
+
+static void do_sdl_resize(int new_width, int new_height, int bpp)
+{
+ int flags;
+
+ // printf("resizing to %d %d\n", w, h);
+
+ flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
+ if (gui_fullscreen)
+ flags |= SDL_FULLSCREEN;
+ if (gui_noframe)
+ flags |= SDL_NOFRAME;
+
+ width = new_width;
+ height = new_height;
+ real_screen = SDL_SetVideoMode(width, height, bpp, flags);
+ if (!real_screen) {
+ fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width,
+ height, bpp, SDL_GetError());
+ exit(1);
+ }
+}
+
+static void sdl_resize(DisplayState *ds)
+{
+ if (!allocator) {
+ if (!scaling_active)
+ do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0);
+ else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds))
+ do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds));
+ sdl_setdata(ds);
+ } else {
+ if (guest_screen != NULL) {
+ SDL_FreeSurface(guest_screen);
+ guest_screen = NULL;
+ }
+ }
+}
+
+static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf)
+{
+ PixelFormat qemu_pf;
+
+ memset(&qemu_pf, 0x00, sizeof(PixelFormat));
+
+ qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel;
+ qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel;
+ qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel);
+
+ qemu_pf.rmask = sdl_pf->Rmask;
+ qemu_pf.gmask = sdl_pf->Gmask;
+ qemu_pf.bmask = sdl_pf->Bmask;
+ qemu_pf.amask = sdl_pf->Amask;
+
+ qemu_pf.rshift = sdl_pf->Rshift;
+ qemu_pf.gshift = sdl_pf->Gshift;
+ qemu_pf.bshift = sdl_pf->Bshift;
+ qemu_pf.ashift = sdl_pf->Ashift;
+
+ qemu_pf.rbits = 8 - sdl_pf->Rloss;
+ qemu_pf.gbits = 8 - sdl_pf->Gloss;
+ qemu_pf.bbits = 8 - sdl_pf->Bloss;
+ qemu_pf.abits = 8 - sdl_pf->Aloss;
+
+ qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1);
+ qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1);
+ qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1);
+ qemu_pf.amax = ((1 << qemu_pf.abits) - 1);
+
+ return qemu_pf;
+}
+
+static DisplaySurface* sdl_create_displaysurface(int width, int height)
+{
+ DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
+ if (surface == NULL) {
+ fprintf(stderr, "sdl_create_displaysurface: malloc failed\n");
+ exit(1);
+ }
+
+ surface->width = width;
+ surface->height = height;
+
+ if (scaling_active) {
+ if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) {
+ surface->linesize = width * 4;
+ surface->pf = qemu_default_pixelformat(32);
+ } else {
+ surface->linesize = width * host_format.BytesPerPixel;
+ surface->pf = sdl_to_qemu_pixelformat(&host_format);
+ }
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_ALLOCATED_FLAG;
+#endif
+ surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height);
+
+ return surface;
+ }
+
+ if (host_format.BitsPerPixel == 16)
+ do_sdl_resize(width, height, 16);
+ else
+ do_sdl_resize(width, height, 32);
+
+ surface->pf = sdl_to_qemu_pixelformat(real_screen->format);
+ surface->linesize = real_screen->pitch;
+ surface->data = real_screen->pixels;
+
+#ifdef HOST_WORDS_BIGENDIAN
+ surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG;
+#else
+ surface->flags = QEMU_REALPIXELS_FLAG;
+#endif
+ allocator = 1;
+
+ return surface;
+}
+
+static void sdl_free_displaysurface(DisplaySurface *surface)
+{
+ allocator = 0;
+ if (surface == NULL)
+ return;
+
+ if (surface->flags & QEMU_ALLOCATED_FLAG)
+ qemu_free(surface->data);
+ qemu_free(surface);
+}
+
+static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height)
+{
+ sdl_free_displaysurface(surface);
+ return sdl_create_displaysurface(width, height);
+}
+
+/* generic keyboard conversion */
+
+#include "sdl_keysym.h"
+
+static kbd_layout_t *kbd_layout = NULL;
+
+static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
+{
+ int keysym;
+ /* workaround for X11+SDL bug with AltGR */
+ keysym = ev->keysym.sym;
+ if (keysym == 0 && ev->keysym.scancode == 113)
+ keysym = SDLK_MODE;
+ /* For Japanese key '\' and '|' */
+ if (keysym == 92 && ev->keysym.scancode == 133) {
+ keysym = 0xa5;
+ }
+ return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
+}
+
+/* specific keyboard conversions from scan codes */
+
+#if defined(_WIN32)
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ return ev->keysym.scancode;
+}
+
+#else
+
+#if defined(SDL_VIDEO_DRIVER_X11)
+#include <X11/XKBlib.h>
+
+static int check_for_evdev(void)
+{
+ SDL_SysWMinfo info;
+ XkbDescPtr desc = NULL;
+ int has_evdev = 0;
+ char *keycodes = NULL;
+
+ SDL_VERSION(&info.version);
+ if (!SDL_GetWMInfo(&info)) {
+ return 0;
+ }
+ desc = XkbGetKeyboard(info.info.x11.display,
+ XkbGBN_AllComponentsMask,
+ XkbUseCoreKbd);
+ if (desc && desc->names) {
+ keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
+ if (keycodes == NULL) {
+ fprintf(stderr, "could not lookup keycode name\n");
+ } else if (strstart(keycodes, "evdev", NULL)) {
+ has_evdev = 1;
+ } else if (!strstart(keycodes, "xfree86", NULL)) {
+ fprintf(stderr, "unknown keycodes `%s', please report to "
+ "qemu-devel@nongnu.org\n", keycodes);
+ }
+ }
+
+ if (desc) {
+ XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
+ }
+ if (keycodes) {
+ XFree(keycodes);
+ }
+ return has_evdev;
+}
+#else
+static int check_for_evdev(void)
+{
+ return 0;
+}
+#endif
+
+static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
+{
+ int keycode;
+ static int has_evdev = -1;
+
+ if (has_evdev == -1)
+ has_evdev = check_for_evdev();
+
+ keycode = ev->keysym.scancode;
+
+ if (keycode < 9) {
+ keycode = 0;
+ } else if (keycode < 97) {
+ keycode -= 8; /* just an offset */
+ } else if (keycode < 158) {
+ /* use conversion table */
+ if (has_evdev)
+ keycode = translate_evdev_keycode(keycode - 97);
+ else
+ keycode = translate_xfree86_keycode(keycode - 97);
+ } else if (keycode == 208) { /* Hiragana_Katakana */
+ keycode = 0x70;
+ } else if (keycode == 211) { /* backslash */
+ keycode = 0x73;
+ } else {
+ keycode = 0;
+ }
+ return keycode;
+}
+
+#endif
+
+static void reset_keys(void)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (modifiers_state[i]) {
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
+ modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void sdl_process_key(SDL_KeyboardEvent *ev)
+{
+ int keycode, v;
+
+ if (ev->keysym.sym == SDLK_PAUSE) {
+ /* specific case */
+ v = 0;
+ if (ev->type == SDL_KEYUP)
+ v |= SCANCODE_UP;
+ kbd_put_keycode(0xe1);
+ kbd_put_keycode(0x1d | v);
+ kbd_put_keycode(0x45 | v);
+ return;
+ }
+
+ if (kbd_layout) {
+ keycode = sdl_keyevent_to_keycode_generic(ev);
+ } else {
+ keycode = sdl_keyevent_to_keycode(ev);
+ }
+
+ switch(keycode) {
+ case 0x00:
+ /* sent when leaving window: reset the modifiers state */
+ reset_keys();
+ return;
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (ev->type == SDL_KEYUP)
+ modifiers_state[keycode] = 0;
+ else
+ modifiers_state[keycode] = 1;
+ break;
+ case 0x45: /* num lock */
+ case 0x3a: /* caps lock */
+ /* SDL does not send the key up event, so we generate it */
+ kbd_put_keycode(keycode);
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ return;
+ }
+
+ /* now send the key code */
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ if (ev->type == SDL_KEYUP)
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ else
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+}
+
+static void sdl_update_caption(void)
+{
+ char win_title[1024];
+ char icon_title[1024];
+ const char *status = "";
+
+ if (!vm_running)
+ status = " [Stopped]";
+ else if (gui_grab) {
+ if (alt_grab)
+ status = " - Press Ctrl-Alt-Shift to exit mouse grab";
+ else if (ctrl_grab)
+ status = " - Press Right-Ctrl to exit mouse grab";
+ else
+ status = " - Press Ctrl-Alt to exit mouse grab";
+ }
+
+ if (qemu_name) {
+ snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
+ } else {
+ snprintf(win_title, sizeof(win_title), "QEMU%s", status);
+ snprintf(icon_title, sizeof(icon_title), "QEMU");
+ }
+
+ SDL_WM_SetCaption(win_title, icon_title);
+}
+
+static void sdl_hide_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ SDL_SetCursor(sdl_cursor_hidden);
+ } else {
+ SDL_ShowCursor(0);
+ }
+}
+
+static void sdl_show_cursor(void)
+{
+ if (!cursor_hide)
+ return;
+
+ if (!kbd_mouse_is_absolute()) {
+ SDL_ShowCursor(1);
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+ else
+ SDL_SetCursor(sdl_cursor_normal);
+ }
+}
+
+static void sdl_grab_start(void)
+{
+ if (guest_cursor) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(guest_x, guest_y);
+ } else
+ sdl_hide_cursor();
+
+ if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
+ gui_grab = 1;
+ sdl_update_caption();
+ } else
+ sdl_show_cursor();
+}
+
+static void sdl_grab_end(void)
+{
+ SDL_WM_GrabInput(SDL_GRAB_OFF);
+ gui_grab = 0;
+ sdl_show_cursor();
+ sdl_update_caption();
+}
+
+static void sdl_mouse_mode_change(Notifier *notify)
+{
+ if (kbd_mouse_is_absolute()) {
+ if (!absolute_enabled) {
+ sdl_hide_cursor();
+ if (gui_grab) {
+ sdl_grab_end();
+ }
+ absolute_enabled = 1;
+ }
+ } else if (absolute_enabled) {
+ sdl_show_cursor();
+ absolute_enabled = 0;
+ }
+}
+
+static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
+{
+ int buttons;
+ buttons = 0;
+ if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
+ buttons |= MOUSE_EVENT_MBUTTON;
+
+ if (kbd_mouse_is_absolute()) {
+ dx = x * 0x7FFF / (width - 1);
+ dy = y * 0x7FFF / (height - 1);
+ } else if (guest_cursor) {
+ x -= guest_x;
+ y -= guest_y;
+ guest_x += x;
+ guest_y += y;
+ dx = x;
+ dy = y;
+ }
+
+ kbd_mouse_event(dx, dy, dz, buttons);
+}
+
+static void toggle_full_screen(DisplayState *ds)
+{
+ gui_fullscreen = !gui_fullscreen;
+ do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel);
+ if (gui_fullscreen) {
+ scaling_active = 0;
+ gui_saved_grab = gui_grab;
+ sdl_grab_start();
+ } else {
+ if (!gui_saved_grab)
+ sdl_grab_end();
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void sdl_refresh(DisplayState *ds)
+{
+ SDL_Event ev1, *ev = &ev1;
+ int mod_state;
+ int buttonstate = SDL_GetMouseState(NULL, NULL);
+
+ if (last_vm_running != vm_running) {
+ last_vm_running = vm_running;
+ sdl_update_caption();
+ }
+
+ vga_hw_update();
+ SDL_EnableUNICODE(!is_graphic_console());
+
+ while (SDL_PollEvent(ev)) {
+ switch (ev->type) {
+ case SDL_VIDEOEXPOSE:
+ sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ if (ev->type == SDL_KEYDOWN) {
+ if (alt_grab) {
+ mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
+ (gui_grab_code | KMOD_LSHIFT);
+ } else if (ctrl_grab) {
+ mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
+ } else {
+ mod_state = (SDL_GetModState() & gui_grab_code) ==
+ gui_grab_code;
+ }
+ gui_key_modifier_pressed = mod_state;
+ if (gui_key_modifier_pressed) {
+ int keycode;
+ keycode = sdl_keyevent_to_keycode(&ev->key);
+ switch(keycode) {
+ case 0x21: /* 'f' key on US keyboard */
+ toggle_full_screen(ds);
+ gui_keysym = 1;
+ break;
+ case 0x16: /* 'u' key on US keyboard */
+ scaling_active = 0;
+ sdl_resize(ds);
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ /* Reset the modifiers sent to the current console */
+ reset_keys();
+ console_select(keycode - 0x02);
+ if (!is_graphic_console()) {
+ /* display grab if going to a text console */
+ if (gui_grab)
+ sdl_grab_end();
+ }
+ gui_keysym = 1;
+ break;
+ default:
+ break;
+ }
+ } else if (!is_graphic_console()) {
+ int keysym;
+ keysym = 0;
+ if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
+ default: break;
+ }
+ } else {
+ switch(ev->key.keysym.sym) {
+ case SDLK_UP: keysym = QEMU_KEY_UP; break;
+ case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
+ case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
+ case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
+ case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
+ case SDLK_END: keysym = QEMU_KEY_END; break;
+ case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
+ case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
+ case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;
+ case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
+ default: break;
+ }
+ }
+ if (keysym) {
+ kbd_put_keysym(keysym);
+ } else if (ev->key.keysym.unicode != 0) {
+ kbd_put_keysym(ev->key.keysym.unicode);
+ }
+ }
+ } else if (ev->type == SDL_KEYUP) {
+ if (!alt_grab) {
+ mod_state = (ev->key.keysym.mod & gui_grab_code);
+ } else {
+ mod_state = (ev->key.keysym.mod &
+ (gui_grab_code | KMOD_LSHIFT));
+ }
+ if (!mod_state) {
+ if (gui_key_modifier_pressed) {
+ gui_key_modifier_pressed = 0;
+ if (gui_keysym == 0) {
+ /* exit/enter grab if pressing Ctrl-Alt */
+ if (!gui_grab) {
+ /* if the application is not active,
+ do not try to enter grab state. It
+ prevents
+ 'SDL_WM_GrabInput(SDL_GRAB_ON)'
+ from blocking all the application
+ (SDL bug). */
+ if (SDL_GetAppState() & SDL_APPACTIVE)
+ sdl_grab_start();
+ } else {
+ sdl_grab_end();
+ }
+ /* SDL does not send back all the
+ modifiers key, so we must correct it */
+ reset_keys();
+ break;
+ }
+ gui_keysym = 0;
+ }
+ }
+ }
+ if (is_graphic_console() && !gui_keysym)
+ sdl_process_key(&ev->key);
+ break;
+ case SDL_QUIT:
+ if (!no_quit)
+ qemu_system_shutdown_request();
+ break;
+ case SDL_MOUSEMOTION:
+ if (gui_grab || kbd_mouse_is_absolute() ||
+ absolute_enabled) {
+ sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
+ ev->motion.x, ev->motion.y, ev->motion.state);
+ }
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ {
+ SDL_MouseButtonEvent *bev = &ev->button;
+ if (!gui_grab && !kbd_mouse_is_absolute()) {
+ if (ev->type == SDL_MOUSEBUTTONDOWN &&
+ (bev->button == SDL_BUTTON_LEFT)) {
+ /* start grabbing all events */
+ sdl_grab_start();
+ }
+ } else {
+ int dz;
+ dz = 0;
+ if (ev->type == SDL_MOUSEBUTTONDOWN) {
+ buttonstate |= SDL_BUTTON(bev->button);
+ } else {
+ buttonstate &= ~SDL_BUTTON(bev->button);
+ }
+#ifdef SDL_BUTTON_WHEELUP
+ if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = -1;
+ } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
+ dz = 1;
+ }
+#endif
+ sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
+ }
+ }
+ break;
+ case SDL_ACTIVEEVENT:
+ if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
+ !ev->active.gain && !gui_fullscreen_initial_grab) {
+ sdl_grab_end();
+ }
+ if (ev->active.state & SDL_APPACTIVE) {
+ if (ev->active.gain) {
+ /* Back to default interval */
+ dcl->gui_timer_interval = 0;
+ dcl->idle = 0;
+ } else {
+ /* Sleeping interval */
+ dcl->gui_timer_interval = 500;
+ dcl->idle = 1;
+ }
+ }
+ break;
+ case SDL_VIDEORESIZE:
+ {
+ SDL_ResizeEvent *rev = &ev->resize;
+ int bpp = real_screen->format->BitsPerPixel;
+ if (bpp != 16 && bpp != 32)
+ bpp = 32;
+ do_sdl_resize(rev->w, rev->h, bpp);
+ scaling_active = 1;
+ if (!is_buffer_shared(ds->surface)) {
+ ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds));
+ dpy_resize(ds);
+ }
+ vga_hw_invalidate();
+ vga_hw_update();
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
+{
+ SDL_Rect dst = { x, y, w, h };
+ SDL_FillRect(real_screen, &dst, c);
+}
+
+static void sdl_mouse_warp(int x, int y, int on)
+{
+ if (on) {
+ if (!guest_cursor)
+ sdl_show_cursor();
+ if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
+ SDL_SetCursor(guest_sprite);
+ if (!kbd_mouse_is_absolute() && !absolute_enabled)
+ SDL_WarpMouse(x, y);
+ }
+ } else if (gui_grab)
+ sdl_hide_cursor();
+ guest_cursor = on;
+ guest_x = x, guest_y = y;
+}
+
+static void sdl_mouse_define(QEMUCursor *c)
+{
+ uint8_t *image, *mask;
+ int bpl;
+
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+
+ bpl = cursor_get_mono_bpl(c);
+ image = qemu_mallocz(bpl * c->height);
+ mask = qemu_mallocz(bpl * c->height);
+ cursor_get_mono_image(c, 0x000000, image);
+ cursor_get_mono_mask(c, 0, mask);
+ guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
+ c->hot_x, c->hot_y);
+ qemu_free(image);
+ qemu_free(mask);
+
+ if (guest_cursor &&
+ (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
+ SDL_SetCursor(guest_sprite);
+}
+
+static void sdl_cleanup(void)
+{
+ if (guest_sprite)
+ SDL_FreeCursor(guest_sprite);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
+{
+ int flags;
+ uint8_t data = 0;
+ DisplayAllocator *da;
+ const SDL_VideoInfo *vi;
+
+#if defined(__APPLE__)
+ /* always use generic keymaps */
+ if (!keyboard_layout)
+ keyboard_layout = "en-us";
+#endif
+ if(keyboard_layout) {
+ kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ if (!kbd_layout)
+ exit(1);
+ }
+
+ if (no_frame)
+ gui_noframe = 1;
+
+ if (!full_screen) {
+ setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
+ }
+
+ flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
+ if (SDL_Init (flags)) {
+ fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
+ SDL_GetError());
+ exit(1);
+ }
+ vi = SDL_GetVideoInfo();
+ host_format = *(vi->vfmt);
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+ dcl->dpy_update = sdl_update;
+ dcl->dpy_resize = sdl_resize;
+ dcl->dpy_refresh = sdl_refresh;
+ dcl->dpy_setdata = sdl_setdata;
+ dcl->dpy_fill = sdl_fill;
+ ds->mouse_set = sdl_mouse_warp;
+ ds->cursor_define = sdl_mouse_define;
+ register_displaychangelistener(ds, dcl);
+
+ da = qemu_mallocz(sizeof(DisplayAllocator));
+ da->create_displaysurface = sdl_create_displaysurface;
+ da->resize_displaysurface = sdl_resize_displaysurface;
+ da->free_displaysurface = sdl_free_displaysurface;
+ if (register_displayallocator(ds, da) == da) {
+ dpy_resize(ds);
+ }
+
+ mouse_mode_notifier.notify = sdl_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
+
+ sdl_update_caption();
+ SDL_EnableKeyRepeat(250, 50);
+ gui_grab = 0;
+
+ sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
+ sdl_cursor_normal = SDL_GetCursor();
+
+ atexit(sdl_cleanup);
+ if (full_screen) {
+ gui_fullscreen = 1;
+ gui_fullscreen_initial_grab = 1;
+ sdl_grab_start();
+ }
+}
diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h
new file mode 100644
index 0000000000..ee904805da
--- /dev/null
+++ b/ui/sdl_keysym.h
@@ -0,0 +1,277 @@
+
+#include "keymaps.h"
+
+static const name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", SDLK_EURO},
+
+ /* modifiers */
+{"Control_L", SDLK_LCTRL},
+{"Control_R", SDLK_RCTRL},
+{"Alt_L", SDLK_LALT},
+{"Alt_R", SDLK_RALT},
+{"Caps_Lock", SDLK_CAPSLOCK},
+{"Meta_L", SDLK_LMETA},
+{"Meta_R", SDLK_RMETA},
+{"Shift_L", SDLK_LSHIFT},
+{"Shift_R", SDLK_RSHIFT},
+{"Super_L", SDLK_LSUPER},
+{"Super_R", SDLK_RSUPER},
+
+ /* special keys */
+{"BackSpace", SDLK_BACKSPACE},
+{"Tab", SDLK_TAB},
+{"Return", SDLK_RETURN},
+{"Right", SDLK_RIGHT},
+{"Left", SDLK_LEFT},
+{"Up", SDLK_UP},
+{"Down", SDLK_DOWN},
+{"Page_Down", SDLK_PAGEDOWN},
+{"Page_Up", SDLK_PAGEUP},
+{"Insert", SDLK_INSERT},
+{"Delete", SDLK_DELETE},
+{"Home", SDLK_HOME},
+{"End", SDLK_END},
+{"Scroll_Lock", SDLK_SCROLLOCK},
+{"F1", SDLK_F1},
+{"F2", SDLK_F2},
+{"F3", SDLK_F3},
+{"F4", SDLK_F4},
+{"F5", SDLK_F5},
+{"F6", SDLK_F6},
+{"F7", SDLK_F7},
+{"F8", SDLK_F8},
+{"F9", SDLK_F9},
+{"F10", SDLK_F10},
+{"F11", SDLK_F11},
+{"F12", SDLK_F12},
+{"F13", SDLK_F13},
+{"F14", SDLK_F14},
+{"F15", SDLK_F15},
+{"Sys_Req", SDLK_SYSREQ},
+{"KP_0", SDLK_KP0},
+{"KP_1", SDLK_KP1},
+{"KP_2", SDLK_KP2},
+{"KP_3", SDLK_KP3},
+{"KP_4", SDLK_KP4},
+{"KP_5", SDLK_KP5},
+{"KP_6", SDLK_KP6},
+{"KP_7", SDLK_KP7},
+{"KP_8", SDLK_KP8},
+{"KP_9", SDLK_KP9},
+{"KP_Add", SDLK_KP_PLUS},
+{"KP_Decimal", SDLK_KP_PERIOD},
+{"KP_Divide", SDLK_KP_DIVIDE},
+{"KP_Enter", SDLK_KP_ENTER},
+{"KP_Equal", SDLK_KP_EQUALS},
+{"KP_Multiply", SDLK_KP_MULTIPLY},
+{"KP_Subtract", SDLK_KP_MINUS},
+{"help", SDLK_HELP},
+{"Menu", SDLK_MENU},
+{"Power", SDLK_POWER},
+{"Print", SDLK_PRINT},
+{"Mode_switch", SDLK_MODE},
+{"Multi_Key", SDLK_COMPOSE},
+{"Num_Lock", SDLK_NUMLOCK},
+{"Pause", SDLK_PAUSE},
+{"Escape", SDLK_ESCAPE},
+
+{NULL, 0},
+};
diff --git a/ui/sdl_zoom.c b/ui/sdl_zoom.c
new file mode 100644
index 0000000000..a986c7c14c
--- /dev/null
+++ b/ui/sdl_zoom.c
@@ -0,0 +1,95 @@
+/*
+ * SDL_zoom - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sdl_zoom.h"
+#include "osdep.h"
+#include <stdint.h>
+#include <stdio.h>
+
+static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect);
+static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect);
+
+#define BPP 32
+#include "sdl_zoom_template.h"
+#undef BPP
+#define BPP 16
+#include "sdl_zoom_template.h"
+#undef BPP
+
+int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth,
+ SDL_Rect *in_rect)
+{
+ SDL_Rect zoom, src_rect;
+ int extra;
+
+ /* Grow the size of the modified rectangle to avoid edge artefacts */
+ src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0;
+ src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0;
+
+ src_rect.w = in_rect->w + 1;
+ if (src_rect.x + src_rect.w > src_sfc->w)
+ src_rect.w = src_sfc->w - src_rect.x;
+
+ src_rect.h = in_rect->h + 1;
+ if (src_rect.y + src_rect.h > src_sfc->h)
+ src_rect.h = src_sfc->h - src_rect.y;
+
+ /* (x,y) : round down */
+ zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w));
+ zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h));
+
+ /* (w,h) : round up */
+ zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) /
+ (double)(src_sfc->w));
+
+ zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) /
+ (double)(src_sfc->h));
+
+ /* Account for any (x,y) rounding by adding one-source-pixel's worth
+ * of destination pixels and then edge checking.
+ */
+
+ extra = ((dst_sfc->w-1) / src_sfc->w) + 1;
+
+ if ((zoom.x + zoom.w) < (dst_sfc->w - extra))
+ zoom.w += extra;
+ else
+ zoom.w = dst_sfc->w - zoom.x;
+
+ extra = ((dst_sfc->h-1) / src_sfc->h) + 1;
+
+ if ((zoom.y + zoom.h) < (dst_sfc->h - extra))
+ zoom.h += extra;
+ else
+ zoom.h = dst_sfc->h - zoom.y;
+
+ /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the
+ * destination surface that needs to be updated.
+ */
+ if (src_sfc->format->BitsPerPixel == 32)
+ sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom);
+ else if (src_sfc->format->BitsPerPixel == 16)
+ sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom);
+ else {
+ fprintf(stderr, "pixel format not supported\n");
+ return -1;
+ }
+
+ /* Return the rectangle of the update to the caller */
+ *in_rect = zoom;
+
+ return 0;
+}
+
diff --git a/ui/sdl_zoom.h b/ui/sdl_zoom.h
new file mode 100644
index 0000000000..74955bc944
--- /dev/null
+++ b/ui/sdl_zoom.h
@@ -0,0 +1,25 @@
+/*
+ * SDL_zoom - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef SDL_zoom_h
+#define SDL_zoom_h
+
+#include <SDL.h>
+
+#define SMOOTHING_OFF 0
+#define SMOOTHING_ON 1
+
+int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc,
+ int smooth, SDL_Rect *src_rect);
+
+#endif /* SDL_zoom_h */
diff --git a/ui/sdl_zoom_template.h b/ui/sdl_zoom_template.h
new file mode 100644
index 0000000000..64bbca849b
--- /dev/null
+++ b/ui/sdl_zoom_template.h
@@ -0,0 +1,225 @@
+/*
+ * SDL_zoom_template - surface scaling
+ *
+ * Copyright (c) 2009 Citrix Systems, Inc.
+ *
+ * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library.
+ * Modifications by Stefano Stabellini.
+ *
+ * This work is licensed under the terms of the GNU GPL version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#if BPP == 16
+#define SDL_TYPE Uint16
+#elif BPP == 32
+#define SDL_TYPE Uint32
+#else
+#error unsupport depth
+#endif
+
+/*
+ * Simple helper functions to make the code looks nicer
+ *
+ * Assume spf = source SDL_PixelFormat
+ * dpf = dest SDL_PixelFormat
+ *
+ */
+#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift)
+#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift)
+#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift)
+#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift)
+
+#define setRed(r, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \
+ (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \
+} while (0);
+
+#define setGreen(g, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \
+ (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \
+} while (0);
+
+#define setBlue(b, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \
+ (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \
+} while (0);
+
+#define setAlpha(a, pcolor) do { \
+ *pcolor = ((*pcolor) & (~(dpf->Amask))) + \
+ (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \
+} while (0);
+
+static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth,
+ SDL_Rect *dst_rect)
+{
+ int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump;
+ SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp;
+ int d_gap;
+ SDL_PixelFormat *spf = src->format;
+ SDL_PixelFormat *dpf = dst->format;
+
+ if (smooth) {
+ /* For interpolation: assume source dimension is one pixel.
+ * Smaller here to avoid overflow on right and bottom edge.
+ */
+ sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
+ sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
+ } else {
+ sx = (int) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (int) (65536.0 * (float) src->h / (float) dst->h);
+ }
+
+ if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
+ return (-1);
+ }
+ if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
+ free(sax);
+ return (-1);
+ }
+
+ sp = csp = (SDL_TYPE *) src->pixels;
+ dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch +
+ dst_rect->x * dst->format->BytesPerPixel);
+
+ csx = 0;
+ csax = sax;
+ for (x = 0; x <= dst->w; x++) {
+ *csax = csx;
+ csax++;
+ csx &= 0xffff;
+ csx += sx;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y <= dst->h; y++) {
+ *csay = csy;
+ csay++;
+ csy &= 0xffff;
+ csy += sy;
+ }
+
+ d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel;
+
+ if (smooth) {
+ csay = say;
+ for (y = 0; y < dst_rect->y; y++) {
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+ }
+
+ /* Calculate sstep_jump */
+ csax = sax;
+ sstep_jump = 0;
+ for (x = 0; x < dst_rect->x; x++) {
+ csax++;
+ sstep = (*csax >> 16);
+ sstep_jump += sstep;
+ }
+
+ for (y = 0; y < dst_rect->h ; y++) {
+ /* Setup colour source pointers */
+ c00 = csp + sstep_jump;
+ c01 = c00 + 1;
+ c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump;
+ c11 = c10 + 1;
+ csax = sax + dst_rect->x;
+
+ for (x = 0; x < dst_rect->w; x++) {
+
+ /* Interpolate colours */
+ ex = (*csax & 0xffff);
+ ey = (*csay & 0xffff);
+ t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) +
+ getRed(*c00)) & (dpf->Rmask >> dpf->Rshift);
+ t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) +
+ getRed(*c10)) & (dpf->Rmask >> dpf->Rshift);
+ setRed((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) +
+ getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift);
+ t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) +
+ getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift);
+ setGreen((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) +
+ getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift);
+ t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) +
+ getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift);
+ setBlue((((t2 - t1) * ey) >> 16) + t1, dp);
+ t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) +
+ getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift);
+ t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) +
+ getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift);
+ setAlpha((((t2 - t1) * ey) >> 16) + t1, dp);
+
+ /* Advance source pointers */
+ csax++;
+ sstep = (*csax >> 16);
+ c00 += sstep;
+ c01 += sstep;
+ c10 += sstep;
+ c11 += sstep;
+ /* Advance destination pointer */
+ dp++;
+ }
+ /* Advance source pointer */
+ csay++;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /* Advance destination pointers */
+ dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap);
+ }
+
+
+ } else {
+ csay = say;
+
+ for (y = 0; y < dst_rect->y; y++) {
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+ }
+
+ /* Calculate sstep_jump */
+ csax = sax;
+ sstep_jump = 0;
+ for (x = 0; x < dst_rect->x; x++) {
+ csax++;
+ sstep = (*csax >> 16);
+ sstep_jump += sstep;
+ }
+
+ for (y = 0 ; y < dst_rect->h ; y++) {
+ sp = csp + sstep_jump;
+ csax = sax + dst_rect->x;
+
+ for (x = 0; x < dst_rect->w; x++) {
+
+ /* Draw */
+ *dp = *sp;
+
+ /* Advance source pointers */
+ csax++;
+ sstep = (*csax >> 16);
+ sp += sstep;
+
+ /* Advance destination pointer */
+ dp++;
+ }
+ /* Advance source pointers */
+ csay++;
+ sstep = (*csay >> 16) * src->pitch;
+ csp = (SDL_TYPE *) ((Uint8 *) csp + sstep);
+
+ /* Advance destination pointer */
+ dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap);
+ }
+ }
+
+ free(sax);
+ free(say);
+ return (0);
+}
+
+#undef SDL_TYPE
+
diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c
new file mode 100644
index 0000000000..a51ddc8a18
--- /dev/null
+++ b/ui/vnc-auth-sasl.c
@@ -0,0 +1,637 @@
+/*
+ * QEMU VNC display driver: SASL auth protocol
+ *
+ * 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 "vnc.h"
+
+/* Max amount of data we send/recv for SASL steps to prevent DOS */
+#define SASL_DATA_MAX_LEN (1024 * 1024)
+
+
+void vnc_sasl_client_cleanup(VncState *vs)
+{
+ if (vs->sasl.conn) {
+ vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0;
+ vs->sasl.encodedLength = vs->sasl.encodedOffset = 0;
+ vs->sasl.encoded = NULL;
+ free(vs->sasl.username);
+ free(vs->sasl.mechlist);
+ vs->sasl.username = vs->sasl.mechlist = NULL;
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ }
+}
+
+
+long vnc_client_write_sasl(VncState *vs)
+{
+ long ret;
+
+ VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd "
+ "Encoded: %p size %d offset %d\n",
+ vs->output.buffer, vs->output.capacity, vs->output.offset,
+ vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset);
+
+ if (!vs->sasl.encoded) {
+ int err;
+ err = sasl_encode(vs->sasl.conn,
+ (char *)vs->output.buffer,
+ vs->output.offset,
+ (const char **)&vs->sasl.encoded,
+ &vs->sasl.encodedLength);
+ if (err != SASL_OK)
+ return vnc_client_io_error(vs, -1, EIO);
+
+ vs->sasl.encodedOffset = 0;
+ }
+
+ ret = vnc_client_write_buf(vs,
+ vs->sasl.encoded + vs->sasl.encodedOffset,
+ vs->sasl.encodedLength - vs->sasl.encodedOffset);
+ if (!ret)
+ return 0;
+
+ vs->sasl.encodedOffset += ret;
+ if (vs->sasl.encodedOffset == vs->sasl.encodedLength) {
+ vs->output.offset = 0;
+ vs->sasl.encoded = NULL;
+ vs->sasl.encodedOffset = vs->sasl.encodedLength = 0;
+ }
+
+ /* Can't merge this block with one above, because
+ * someone might have written more unencrypted
+ * data in vs->output while we were processing
+ * SASL encoded output
+ */
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+
+ return ret;
+}
+
+
+long vnc_client_read_sasl(VncState *vs)
+{
+ long ret;
+ uint8_t encoded[4096];
+ const char *decoded;
+ unsigned int decodedLen;
+ int err;
+
+ ret = vnc_client_read_buf(vs, encoded, sizeof(encoded));
+ if (!ret)
+ return 0;
+
+ err = sasl_decode(vs->sasl.conn,
+ (char *)encoded, ret,
+ &decoded, &decodedLen);
+
+ if (err != SASL_OK)
+ return vnc_client_io_error(vs, -1, -EIO);
+ VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n",
+ encoded, ret, decoded, decodedLen);
+ buffer_reserve(&vs->input, decodedLen);
+ buffer_append(&vs->input, decoded, decodedLen);
+ return decodedLen;
+}
+
+
+static int vnc_auth_sasl_check_access(VncState *vs)
+{
+ const void *val;
+ int err;
+ int allow;
+
+ err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n",
+ err, sasl_errstring(err, NULL, NULL));
+ return -1;
+ }
+ if (val == NULL) {
+ VNC_DEBUG("no client username was found, denying access\n");
+ return -1;
+ }
+ VNC_DEBUG("SASL client username %s\n", (const char *)val);
+
+ vs->sasl.username = qemu_strdup((const char*)val);
+
+ if (vs->vd->sasl.acl == NULL) {
+ VNC_DEBUG("no ACL activated, allowing access\n");
+ return 0;
+ }
+
+ allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username);
+
+ VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username,
+ allow ? "allowed" : "denied");
+ return allow ? 0 : -1;
+}
+
+static int vnc_auth_sasl_check_ssf(VncState *vs)
+{
+ const void *val;
+ int err, ssf;
+
+ if (!vs->sasl.wantSSF)
+ return 1;
+
+ err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val);
+ if (err != SASL_OK)
+ return 0;
+
+ ssf = *(const int *)val;
+ VNC_DEBUG("negotiated an SSF of %d\n", ssf);
+ if (ssf < 56)
+ return 0; /* 56 is good for Kerberos */
+
+ /* Only setup for read initially, because we're about to send an RPC
+ * reply which must be in plain text. When the next incoming RPC
+ * arrives, we'll switch on writes too
+ *
+ * cf qemudClientReadSASL in qemud.c
+ */
+ vs->sasl.runSSF = 1;
+
+ /* We have a SSF that's good enough */
+ return 1;
+}
+
+/*
+ * Step Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len);
+
+static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t datalen = len;
+ const char *serverout;
+ unsigned int serveroutlen;
+ int err;
+ char *clientdata = NULL;
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (datalen) {
+ clientdata = (char*)data;
+ clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */
+ datalen--; /* Don't count NULL byte when passing to _start() */
+ }
+
+ VNC_DEBUG("Step using SASL Data %p (%d bytes)\n",
+ clientdata, datalen);
+ err = sasl_server_step(vs->sasl.conn,
+ clientdata,
+ datalen,
+ &serverout,
+ &serveroutlen);
+ if (err != SASL_OK &&
+ err != SASL_CONTINUE) {
+ VNC_DEBUG("sasl step failed %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ if (serveroutlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("sasl step reply data too long %d\n",
+ serveroutlen);
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
+ serveroutlen, serverout ? 0 : 1);
+
+ if (serveroutlen) {
+ vnc_write_u32(vs, serveroutlen + 1);
+ vnc_write(vs, serverout, serveroutlen + 1);
+ } else {
+ vnc_write_u32(vs, 0);
+ }
+
+ /* Whether auth is complete */
+ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
+
+ if (err == SASL_CONTINUE) {
+ VNC_DEBUG("%s", "Authentication must continue\n");
+ /* Wait for step length */
+ 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);
+ goto authreject;
+ }
+
+ /* Check username whitelist ACL */
+ if (vnc_auth_sasl_check_access(vs) < 0) {
+ VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ goto authreject;
+ }
+
+ VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ vnc_write_u32(vs, 0); /* Accept auth */
+ /*
+ * Delay writing in SSF encoded mode until pending output
+ * buffer is written
+ */
+ if (vs->sasl.runSSF)
+ vs->sasl.waitWriteSSF = vs->output.offset;
+ start_client_init(vs);
+ }
+
+ return 0;
+
+ authreject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ vnc_write_u32(vs, sizeof("Authentication failed"));
+ vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return -1;
+
+ authabort:
+ vnc_client_error(vs);
+ return -1;
+}
+
+static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t steplen = read_u32(data, 0);
+ VNC_DEBUG("Got client step len %d\n", steplen);
+ if (steplen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("Too much SASL data %d\n", steplen);
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (steplen == 0)
+ return protocol_client_auth_sasl_step(vs, NULL, 0);
+ else
+ vnc_read_when(vs, protocol_client_auth_sasl_step, steplen);
+ return 0;
+}
+
+/*
+ * Start Msg
+ *
+ * Input from client:
+ *
+ * u32 clientin-length
+ * u8-array clientin-string
+ *
+ * Output to client:
+ *
+ * u32 serverout-length
+ * u8-array serverout-strin
+ * u8 continue
+ */
+
+#define SASL_DATA_MAX_LEN (1024 * 1024)
+
+static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t datalen = len;
+ const char *serverout;
+ unsigned int serveroutlen;
+ int err;
+ char *clientdata = NULL;
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (datalen) {
+ clientdata = (char*)data;
+ clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */
+ datalen--; /* Don't count NULL byte when passing to _start() */
+ }
+
+ VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n",
+ vs->sasl.mechlist, clientdata, datalen);
+ err = sasl_server_start(vs->sasl.conn,
+ vs->sasl.mechlist,
+ clientdata,
+ datalen,
+ &serverout,
+ &serveroutlen);
+ if (err != SASL_OK &&
+ err != SASL_CONTINUE) {
+ VNC_DEBUG("sasl start failed %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ if (serveroutlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("sasl start reply data too long %d\n",
+ serveroutlen);
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ VNC_DEBUG("SASL return data %d bytes, nil; %d\n",
+ serveroutlen, serverout ? 0 : 1);
+
+ if (serveroutlen) {
+ vnc_write_u32(vs, serveroutlen + 1);
+ vnc_write(vs, serverout, serveroutlen + 1);
+ } else {
+ vnc_write_u32(vs, 0);
+ }
+
+ /* Whether auth is complete */
+ vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1);
+
+ if (err == SASL_CONTINUE) {
+ VNC_DEBUG("%s", "Authentication must continue\n");
+ /* Wait for step length */
+ 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);
+ goto authreject;
+ }
+
+ /* Check username whitelist ACL */
+ if (vnc_auth_sasl_check_access(vs) < 0) {
+ VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock);
+ goto authreject;
+ }
+
+ VNC_DEBUG("Authentication successful %d\n", vs->csock);
+ vnc_write_u32(vs, 0); /* Accept auth */
+ start_client_init(vs);
+ }
+
+ return 0;
+
+ authreject:
+ vnc_write_u32(vs, 1); /* Reject auth */
+ vnc_write_u32(vs, sizeof("Authentication failed"));
+ vnc_write(vs, "Authentication failed", sizeof("Authentication failed"));
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return -1;
+
+ authabort:
+ vnc_client_error(vs);
+ return -1;
+}
+
+static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t startlen = read_u32(data, 0);
+ VNC_DEBUG("Got client start len %d\n", startlen);
+ if (startlen > SASL_DATA_MAX_LEN) {
+ VNC_DEBUG("Too much SASL data %d\n", startlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (startlen == 0)
+ return protocol_client_auth_sasl_start(vs, NULL, 0);
+
+ vnc_read_when(vs, protocol_client_auth_sasl_start, startlen);
+ return 0;
+}
+
+static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len)
+{
+ char *mechname = malloc(len + 1);
+ if (!mechname) {
+ VNC_DEBUG("Out of memory reading mechname\n");
+ vnc_client_error(vs);
+ }
+ strncpy(mechname, (char*)data, len);
+ mechname[len] = '\0';
+ VNC_DEBUG("Got client mechname '%s' check against '%s'\n",
+ mechname, vs->sasl.mechlist);
+
+ if (strncmp(vs->sasl.mechlist, mechname, len) == 0) {
+ if (vs->sasl.mechlist[len] != '\0' &&
+ vs->sasl.mechlist[len] != ',') {
+ VNC_DEBUG("One %d", vs->sasl.mechlist[len]);
+ vnc_client_error(vs);
+ return -1;
+ }
+ } else {
+ char *offset = strstr(vs->sasl.mechlist, mechname);
+ VNC_DEBUG("Two %p\n", offset);
+ if (!offset) {
+ vnc_client_error(vs);
+ return -1;
+ }
+ VNC_DEBUG("Two '%s'\n", offset);
+ if (offset[-1] != ',' ||
+ (offset[len] != '\0'&&
+ offset[len] != ',')) {
+ vnc_client_error(vs);
+ return -1;
+ }
+ }
+
+ free(vs->sasl.mechlist);
+ vs->sasl.mechlist = mechname;
+
+ VNC_DEBUG("Validated mechname '%s'\n", mechname);
+ vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4);
+ return 0;
+}
+
+static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len)
+{
+ uint32_t mechlen = read_u32(data, 0);
+ VNC_DEBUG("Got client mechname len %d\n", mechlen);
+ if (mechlen > 100) {
+ VNC_DEBUG("Too long SASL mechname data %d\n", mechlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+ if (mechlen < 1) {
+ VNC_DEBUG("Too short SASL mechname %d\n", mechlen);
+ vnc_client_error(vs);
+ return -1;
+ }
+ vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen);
+ return 0;
+}
+
+#define USES_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)
+
+
+void start_auth_sasl(VncState *vs)
+{
+ const char *mechlist = NULL;
+ sasl_security_properties_t secprops;
+ int err;
+ char *localAddr, *remoteAddr;
+ int mechlistlen;
+
+ VNC_DEBUG("Initialize SASL auth %d\n", vs->csock);
+
+ /* Get local & remote client addresses in form IPADDR;PORT */
+ if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock)))
+ goto authabort;
+
+ if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) {
+ free(localAddr);
+ goto authabort;
+ }
+
+ err = sasl_server_new("vnc",
+ NULL, /* FQDN - just delegates to gethostname */
+ NULL, /* User realm */
+ localAddr,
+ remoteAddr,
+ NULL, /* Callbacks, not needed */
+ SASL_SUCCESS_DATA,
+ &vs->sasl.conn);
+ free(localAddr);
+ free(remoteAddr);
+ localAddr = remoteAddr = NULL;
+
+ if (err != SASL_OK) {
+ VNC_DEBUG("sasl context setup failed %d (%s)",
+ err, sasl_errstring(err, NULL, NULL));
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+#ifdef CONFIG_VNC_TLS
+ /* Inform SASL that we've got an external SSF layer from TLS/x509 */
+ if (vs->vd->auth == VNC_AUTH_VENCRYPT &&
+ vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) {
+ gnutls_cipher_algorithm_t cipher;
+ 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");
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ ssf *= 8; /* tls key size is bytes, sasl wants bits */
+
+ err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot set SASL external SSF %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ vs->sasl.wantSSF = 1;
+
+ memset (&secprops, 0, sizeof secprops);
+ /* Inform SASL that we've got an external SSF layer from TLS */
+ if (strncmp(vs->vd->display, "unix:", 5) == 0
+#ifdef CONFIG_VNC_TLS
+ /* Disable SSF, if using TLS+x509+SASL only. TLS without x509
+ is not sufficiently strong */
+ || (vs->vd->auth == VNC_AUTH_VENCRYPT &&
+ vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+#endif /* CONFIG_VNC_TLS */
+ ) {
+ /* If we've got TLS or UNIX domain sock, we don't care about SSF */
+ secprops.min_ssf = 0;
+ secprops.max_ssf = 0;
+ secprops.maxbufsize = 8192;
+ secprops.security_flags = 0;
+ } else {
+ /* Plain TCP, better get an SSF layer */
+ secprops.min_ssf = 56; /* Good enough to require kerberos */
+ secprops.max_ssf = 100000; /* Arbitrary big number */
+ secprops.maxbufsize = 8192;
+ /* Forbid any anonymous or trivially crackable auth */
+ secprops.security_flags =
+ SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+ }
+
+ err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot set SASL security props %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+
+ err = sasl_listmech(vs->sasl.conn,
+ NULL, /* Don't need to set user */
+ "", /* Prefix */
+ ",", /* Separator */
+ "", /* Suffix */
+ &mechlist,
+ NULL,
+ NULL);
+ if (err != SASL_OK) {
+ VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n",
+ err, sasl_errdetail(vs->sasl.conn));
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist);
+
+ if (!(vs->sasl.mechlist = strdup(mechlist))) {
+ VNC_DEBUG("Out of memory");
+ sasl_dispose(&vs->sasl.conn);
+ vs->sasl.conn = NULL;
+ goto authabort;
+ }
+ mechlistlen = strlen(mechlist);
+ vnc_write_u32(vs, mechlistlen);
+ vnc_write(vs, mechlist, mechlistlen);
+ vnc_flush(vs);
+
+ VNC_DEBUG("Wait for client mechname length\n");
+ vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4);
+
+ return;
+
+ authabort:
+ vnc_client_error(vs);
+ return;
+}
+
+
diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h
new file mode 100644
index 0000000000..fd9b18a8fe
--- /dev/null
+++ b/ui/vnc-auth-sasl.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU VNC display driver: SASL auth protocol
+ *
+ * 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_AUTH_SASL_H__
+#define __QEMU_VNC_AUTH_SASL_H__
+
+
+#include <sasl/sasl.h>
+
+typedef struct VncStateSASL VncStateSASL;
+typedef struct VncDisplaySASL VncDisplaySASL;
+
+#include "acl.h"
+
+struct VncStateSASL {
+ sasl_conn_t *conn;
+ /* If we want to negotiate an SSF layer with client */
+ int wantSSF :1;
+ /* If we are now running the SSF layer */
+ int runSSF :1;
+ /*
+ * If this is non-zero, then wait for that many bytes
+ * to be written plain, before switching to SSF encoding
+ * This allows the VNC auth result to finish being
+ * written in plain.
+ */
+ unsigned int waitWriteSSF;
+
+ /*
+ * Buffering encoded data to allow more clear data
+ * to be stuffed onto the output buffer
+ */
+ const uint8_t *encoded;
+ unsigned int encodedLength;
+ unsigned int encodedOffset;
+ char *username;
+ char *mechlist;
+};
+
+struct VncDisplaySASL {
+ qemu_acl *acl;
+};
+
+void vnc_sasl_client_cleanup(VncState *vs);
+
+long vnc_client_read_sasl(VncState *vs);
+long vnc_client_write_sasl(VncState *vs);
+
+void start_auth_sasl(VncState *vs);
+
+#endif /* __QEMU_VNC_AUTH_SASL_H__ */
+
diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c
new file mode 100644
index 0000000000..07c169186a
--- /dev/null
+++ b/ui/vnc-auth-vencrypt.c
@@ -0,0 +1,175 @@
+/*
+ * QEMU VNC display driver: VeNCrypt authentication setup
+ *
+ * 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 "vnc.h"
+
+
+static void start_auth_vencrypt_subauth(VncState *vs)
+{
+ switch (vs->vd->subauth) {
+ case VNC_AUTH_VENCRYPT_TLSNONE:
+ case VNC_AUTH_VENCRYPT_X509NONE:
+ VNC_DEBUG("Accept TLS auth none\n");
+ vnc_write_u32(vs, 0); /* Accept auth completion */
+ start_client_init(vs);
+ break;
+
+ case VNC_AUTH_VENCRYPT_TLSVNC:
+ case VNC_AUTH_VENCRYPT_X509VNC:
+ VNC_DEBUG("Start TLS auth VNC\n");
+ start_auth_vnc(vs);
+ break;
+
+#ifdef CONFIG_VNC_SASL
+ case VNC_AUTH_VENCRYPT_TLSSASL:
+ case VNC_AUTH_VENCRYPT_X509SASL:
+ VNC_DEBUG("Start TLS auth SASL\n");
+ return start_auth_sasl(vs);
+#endif /* CONFIG_VNC_SASL */
+
+ default: /* Should not be possible, but just in case */
+ VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth);
+ vnc_write_u8(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Unsupported authentication type";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ }
+}
+
+static void vnc_tls_handshake_io(void *opaque);
+
+static int vnc_start_vencrypt_handshake(struct VncState *vs) {
+ 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;
+ }
+
+ 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");
+ }
+ }
+
+ VNC_DEBUG("Handshake done, switching to TLS data mode\n");
+ vs->tls.wiremode = VNC_WIREMODE_TLS;
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+
+ start_auth_vencrypt_subauth(vs);
+
+ return 0;
+}
+
+static void vnc_tls_handshake_io(void *opaque) {
+ struct VncState *vs = (struct VncState *)opaque;
+
+ VNC_DEBUG("Handshake IO continue\n");
+ vnc_start_vencrypt_handshake(vs);
+}
+
+
+
+#define NEED_X509_AUTH(vs) \
+ ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \
+ (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL)
+
+
+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
+{
+ int auth = read_u32(data, 0);
+
+ if (auth != vs->vd->subauth) {
+ VNC_DEBUG("Rejecting auth %d\n", auth);
+ vnc_write_u8(vs, 0); /* Reject auth */
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+ 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;
+ }
+
+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n");
+ if (vnc_start_vencrypt_handshake(vs) < 0) {
+ VNC_DEBUG("Failed to start TLS handshake\n");
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
+{
+ if (data[0] != 0 ||
+ data[1] != 2) {
+ VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
+ vnc_write_u8(vs, 1); /* Reject version */
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+ VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth);
+ vnc_write_u8(vs, 0); /* Accept version */
+ vnc_write_u8(vs, 1); /* Number of sub-auths */
+ vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
+ }
+ return 0;
+}
+
+
+void start_auth_vencrypt(VncState *vs)
+{
+ /* Send VeNCrypt version 0.2 */
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 2);
+
+ vnc_read_when(vs, protocol_client_vencrypt_init, 2);
+}
+
diff --git a/ui/vnc-auth-vencrypt.h b/ui/vnc-auth-vencrypt.h
new file mode 100644
index 0000000000..9f674c5173
--- /dev/null
+++ b/ui/vnc-auth-vencrypt.h
@@ -0,0 +1,33 @@
+/*
+ * QEMU VNC display driver
+ *
+ * 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_AUTH_VENCRYPT_H__
+#define __QEMU_VNC_AUTH_VENCRYPT_H__
+
+void start_auth_vencrypt(VncState *vs);
+
+#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */
diff --git a/ui/vnc-encoding-hextile.c b/ui/vnc-encoding-hextile.c
new file mode 100644
index 0000000000..728f25e5d8
--- /dev/null
+++ b/ui/vnc-encoding-hextile.c
@@ -0,0 +1,116 @@
+/*
+ * QEMU VNC display driver: hextile encoding
+ *
+ * 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 "vnc.h"
+
+static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
+{
+ ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F);
+ ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F);
+}
+
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+
+#define GENERIC
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
+ int y, int w, int h)
+{
+ int i, j;
+ int has_fg, has_bg;
+ uint8_t *last_fg, *last_bg;
+ VncDisplay *vd = vs->vd;
+
+ last_fg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
+ last_bg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel);
+ has_fg = has_bg = 0;
+ for (j = y; j < (y + h); j += 16) {
+ for (i = x; i < (x + w); i += 16) {
+ vs->send_hextile_tile(vs, i, j,
+ MIN(16, x + w - i), MIN(16, y + h - j),
+ last_bg, last_fg, &has_bg, &has_fg);
+ }
+ }
+ free(last_fg);
+ free(last_bg);
+
+ return 1;
+}
+
+void vnc_hextile_set_pixel_conversion(VncState *vs, int generic)
+{
+ if (!generic) {
+ switch (vs->ds->surface->pf.bits_per_pixel) {
+ case 8:
+ vs->send_hextile_tile = send_hextile_tile_8;
+ break;
+ case 16:
+ vs->send_hextile_tile = send_hextile_tile_16;
+ break;
+ case 32:
+ vs->send_hextile_tile = send_hextile_tile_32;
+ break;
+ }
+ } else {
+ switch (vs->ds->surface->pf.bits_per_pixel) {
+ case 8:
+ vs->send_hextile_tile = send_hextile_tile_generic_8;
+ break;
+ case 16:
+ vs->send_hextile_tile = send_hextile_tile_generic_16;
+ break;
+ case 32:
+ vs->send_hextile_tile = send_hextile_tile_generic_32;
+ break;
+ }
+ }
+}
diff --git a/ui/vnc-encoding-tight.c b/ui/vnc-encoding-tight.c
new file mode 100644
index 0000000000..c1a292bdd2
--- /dev/null
+++ b/ui/vnc-encoding-tight.c
@@ -0,0 +1,1520 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/libvncserver/tight.c
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
+ *
+ * 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-common.h"
+
+#ifdef CONFIG_VNC_JPEG
+#include <stdio.h>
+#include <jpeglib.h>
+#endif
+
+#include "bswap.h"
+#include "qdict.h"
+#include "qint.h"
+#include "vnc.h"
+#include "vnc-encoding-tight.h"
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+static const struct {
+ int max_rect_size, max_rect_width;
+ int mono_min_rect_size, gradient_min_rect_size;
+ int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level;
+ int gradient_threshold, gradient_threshold24;
+ int idx_max_colors_divisor;
+ int jpeg_quality, jpeg_threshold, jpeg_threshold24;
+} tight_conf[] = {
+ { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
+ { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
+ { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
+ { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
+ { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
+ { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
+ { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
+ { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
+ { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
+ { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
+};
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+static uint
+tight_detect_smooth_image24(VncState *vs, int w, int h)
+{
+ int off;
+ int x, y, d, dx;
+ uint c;
+ uint stats[256];
+ int pixels = 0;
+ int pix, left[3];
+ uint errors;
+ unsigned char *buf = vs->tight.buffer;
+
+ /*
+ * If client is big-endian, color samples begin from the second
+ * byte (offset 1) of a 32-bit pixel value.
+ */
+ off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG);
+
+ memset(stats, 0, sizeof (stats));
+
+ for (y = 0, x = 0; y < h && x < w;) {
+ for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH;
+ d++) {
+ for (c = 0; c < 3; c++) {
+ left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+ }
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) {
+ for (c = 0; c < 3; c++) {
+ pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+ stats[abs(pix - left[c])]++;
+ left[c] = pix;
+ }
+ pixels++;
+ }
+ }
+ if (w > h) {
+ x += h;
+ y = 0;
+ } else {
+ x = 0;
+ y += w;
+ }
+ }
+
+ /* 95% smooth or more ... */
+ if (stats[0] * 33 / pixels >= 95) {
+ return 0;
+ }
+
+ errors = 0;
+ for (c = 1; c < 8; c++) {
+ errors += stats[c] * (c * c);
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) {
+ return 0;
+ }
+ }
+ for (; c < 256; c++) {
+ errors += stats[c] * (c * c);
+ }
+ errors /= (pixels * 3 - stats[0]);
+
+ return errors;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp) \
+ \
+ static uint \
+ tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \
+ bool endian; \
+ uint##bpp##_t pix; \
+ int max[3], shift[3]; \
+ int x, y, d, dx; \
+ uint c; \
+ uint stats[256]; \
+ int pixels = 0; \
+ int sample, sum, left[3]; \
+ uint errors; \
+ unsigned char *buf = vs->tight.buffer; \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ memset(stats, 0, sizeof(stats)); \
+ \
+ y = 0, x = 0; \
+ while (y < h && x < w) { \
+ for (d = 0; d < h - y && \
+ d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ for (c = 0; c < 3; c++) { \
+ left[c] = (int)(pix >> shift[c] & max[c]); \
+ } \
+ for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \
+ dx++) { \
+ pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ sum = 0; \
+ for (c = 0; c < 3; c++) { \
+ sample = (int)(pix >> shift[c] & max[c]); \
+ sum += abs(sample - left[c]); \
+ left[c] = sample; \
+ } \
+ if (sum > 255) { \
+ sum = 255; \
+ } \
+ stats[sum]++; \
+ pixels++; \
+ } \
+ } \
+ if (w > h) { \
+ x += h; \
+ y = 0; \
+ } else { \
+ x = 0; \
+ y += w; \
+ } \
+ } \
+ \
+ if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
+ return 0; \
+ } \
+ \
+ errors = 0; \
+ for (c = 1; c < 8; c++) { \
+ errors += stats[c] * (c * c); \
+ if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \
+ return 0; \
+ } \
+ } \
+ for (; c < 256; c++) { \
+ errors += stats[c] * (c * c); \
+ } \
+ errors /= (pixels - stats[0]); \
+ \
+ return errors; \
+ }
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+static int
+tight_detect_smooth_image(VncState *vs, int w, int h)
+{
+ uint errors;
+ int compression = vs->tight_compression;
+ int quality = vs->tight_quality;
+
+ if (!vs->vd->lossy) {
+ return 0;
+ }
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1 ||
+ vs->clientds.pf.bytes_per_pixel == 1 ||
+ w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) {
+ return 0;
+ }
+
+ if (vs->tight_quality != -1) {
+ if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) {
+ return 0;
+ }
+ } else {
+ if (w * h < tight_conf[compression].gradient_min_rect_size) {
+ return 0;
+ }
+ }
+
+ if (vs->clientds.pf.bytes_per_pixel == 4) {
+ if (vs->tight_pixel24) {
+ errors = tight_detect_smooth_image24(vs, w, h);
+ if (vs->tight_quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold24);
+ }
+ return (errors < tight_conf[compression].gradient_threshold24);
+ } else {
+ errors = tight_detect_smooth_image32(vs, w, h);
+ }
+ } else {
+ errors = tight_detect_smooth_image16(vs, w, h);
+ }
+ if (quality != -1) {
+ return (errors < tight_conf[quality].jpeg_threshold);
+ }
+ return (errors < tight_conf[compression].gradient_threshold);
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+
+static void tight_palette_rgb2buf(uint32_t rgb, int bpp, uint8_t buf[6])
+{
+ memset(buf, 0, 6);
+
+ if (bpp == 32) {
+ buf[0] = ((rgb >> 24) & 0xFF);
+ buf[1] = ((rgb >> 16) & 0xFF);
+ buf[2] = ((rgb >> 8) & 0xFF);
+ buf[3] = ((rgb >> 0) & 0xFF);
+ buf[4] = ((buf[0] & 1) == 0) << 3 | ((buf[1] & 1) == 0) << 2;
+ buf[4]|= ((buf[2] & 1) == 0) << 1 | ((buf[3] & 1) == 0) << 0;
+ buf[0] |= 1;
+ buf[1] |= 1;
+ buf[2] |= 1;
+ buf[3] |= 1;
+ }
+ if (bpp == 16) {
+ buf[0] = ((rgb >> 8) & 0xFF);
+ buf[1] = ((rgb >> 0) & 0xFF);
+ buf[2] = ((buf[0] & 1) == 0) << 1 | ((buf[1] & 1) == 0) << 0;
+ buf[0] |= 1;
+ buf[1] |= 1;
+ }
+}
+
+static uint32_t tight_palette_buf2rgb(int bpp, const uint8_t *buf)
+{
+ uint32_t rgb = 0;
+
+ if (bpp == 32) {
+ rgb |= ((buf[0] & ~1) | !((buf[4] >> 3) & 1)) << 24;
+ rgb |= ((buf[1] & ~1) | !((buf[4] >> 2) & 1)) << 16;
+ rgb |= ((buf[2] & ~1) | !((buf[4] >> 1) & 1)) << 8;
+ rgb |= ((buf[3] & ~1) | !((buf[4] >> 0) & 1)) << 0;
+ }
+ if (bpp == 16) {
+ rgb |= ((buf[0] & ~1) | !((buf[2] >> 1) & 1)) << 8;
+ rgb |= ((buf[1] & ~1) | !((buf[2] >> 0) & 1)) << 0;
+ }
+ return rgb;
+}
+
+
+static int tight_palette_insert(QDict *palette, uint32_t rgb, int bpp, int max)
+{
+ uint8_t key[6];
+ int idx = qdict_size(palette);
+ bool present;
+
+ tight_palette_rgb2buf(rgb, bpp, key);
+ present = qdict_haskey(palette, (char *)key);
+ if (idx >= max && !present) {
+ return 0;
+ }
+ if (!present) {
+ qdict_put(palette, (char *)key, qint_from_int(idx));
+ }
+ return qdict_size(palette);
+}
+
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+ static int \
+ tight_fill_palette##bpp(VncState *vs, int x, int y, \
+ int max, size_t count, \
+ uint32_t *bg, uint32_t *fg, \
+ struct QDict **palette) { \
+ uint##bpp##_t *data; \
+ uint##bpp##_t c0, c1, ci; \
+ int i, n0, n1; \
+ \
+ data = (uint##bpp##_t *)vs->tight.buffer; \
+ \
+ c0 = data[0]; \
+ i = 1; \
+ while (i < count && data[i] == c0) \
+ i++; \
+ if (i >= count) { \
+ *bg = *fg = c0; \
+ return 1; \
+ } \
+ \
+ if (max < 2) { \
+ return 0; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ *bg = (uint32_t)c0; \
+ *fg = (uint32_t)c1; \
+ } else { \
+ *bg = (uint32_t)c1; \
+ *fg = (uint32_t)c0; \
+ } \
+ return 2; \
+ } \
+ \
+ if (max == 2) { \
+ return 0; \
+ } \
+ \
+ *palette = qdict_new(); \
+ tight_palette_insert(*palette, c0, bpp, max); \
+ tight_palette_insert(*palette, c1, bpp, max); \
+ tight_palette_insert(*palette, ci, bpp, max); \
+ \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ continue; \
+ } else { \
+ if (!tight_palette_insert(*palette, (uint32_t)ci, \
+ bpp, max)) { \
+ return 0; \
+ } \
+ ci = data[i]; \
+ } \
+ } \
+ \
+ return qdict_size(*palette); \
+ }
+
+DEFINE_FILL_PALETTE_FUNCTION(8)
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+static int tight_fill_palette(VncState *vs, int x, int y,
+ size_t count, uint32_t *bg, uint32_t *fg,
+ struct QDict **palette)
+{
+ int max;
+
+ max = count / tight_conf[vs->tight_compression].idx_max_colors_divisor;
+ if (max < 2 &&
+ count >= tight_conf[vs->tight_compression].mono_min_rect_size) {
+ max = 2;
+ }
+ if (max >= 256) {
+ max = 256;
+ }
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette);
+ case 2:
+ return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette);
+ default:
+ max = 2;
+ return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette);
+ }
+ return 0;
+}
+
+/* Callback to dump a palette with qdict_iter
+static void print_palette(const char *key, QObject *obj, void *opaque)
+{
+ uint8_t idx = qint_get_int(qobject_to_qint(obj));
+ uint32_t rgb = tight_palette_buf2rgb(32, (uint8_t *)key);
+
+ fprintf(stderr, "%.2x ", (unsigned char)*key);
+ while (*key++)
+ fprintf(stderr, "%.2x ", (unsigned char)*key);
+
+ fprintf(stderr, ": idx: %x rgb: %x\n", idx, rgb);
+}
+*/
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \
+ struct QDict *palette) { \
+ uint##bpp##_t *src; \
+ uint##bpp##_t rgb; \
+ uint8_t key[6]; \
+ int i, rep; \
+ uint8_t idx; \
+ \
+ src = (uint##bpp##_t *) buf; \
+ \
+ for (i = 0; i < count; i++) { \
+ rgb = *src++; \
+ rep = 0; \
+ while (i < count && *src == rgb) { \
+ rep++, src++, i++; \
+ } \
+ tight_palette_rgb2buf(rgb, bpp, key); \
+ if (!qdict_haskey(palette, (char *)key)) { \
+ /* \
+ * Should never happen, but don't break everything \
+ * if it does, use the first color instead \
+ */ \
+ idx = 0; \
+ } else { \
+ idx = qdict_get_int(palette, (char *)key); \
+ } \
+ while (rep >= 0) { \
+ *buf++ = idx; \
+ rep--; \
+ } \
+ } \
+ }
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+ static void \
+ tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \
+ uint##bpp##_t bg, uint##bpp##_t fg) { \
+ uint##bpp##_t *ptr; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (uint##bpp##_t *) buf; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) { \
+ break; \
+ } \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) { \
+ continue; \
+ } \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (uint8_t)value; \
+ } \
+ }
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h)
+{
+ uint32_t *buf32;
+ uint32_t pix32;
+ int shift[3];
+ int *prev;
+ int here[3], upper[3], left[3], upperleft[3];
+ int prediction;
+ int x, y, c;
+
+ buf32 = (uint32_t *)buf;
+ memset(vs->tight_gradient.buffer, 0, w * 3 * sizeof(int));
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ shift[0] = vs->clientds.pf.rshift;
+ shift[1] = vs->clientds.pf.gshift;
+ shift[2] = vs->clientds.pf.bshift;
+ } else {
+ shift[0] = 24 - vs->clientds.pf.rshift;
+ shift[1] = 24 - vs->clientds.pf.gshift;
+ shift[2] = 24 - vs->clientds.pf.bshift;
+ }
+
+ for (y = 0; y < h; y++) {
+ for (c = 0; c < 3; c++) {
+ upper[c] = 0;
+ here[c] = 0;
+ }
+ prev = (int *)vs->tight_gradient.buffer;
+ for (x = 0; x < w; x++) {
+ pix32 = *buf32++;
+ for (c = 0; c < 3; c++) {
+ upperleft[c] = upper[c];
+ left[c] = here[c];
+ upper[c] = *prev;
+ here[c] = (int)(pix32 >> shift[c] & 0xFF);
+ *prev++ = here[c];
+
+ prediction = left[c] + upper[c] - upperleft[c];
+ if (prediction < 0) {
+ prediction = 0;
+ } else if (prediction > 0xFF) {
+ prediction = 0xFF;
+ }
+ *buf++ = (char)(here[c] - prediction);
+ }
+ }
+ }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
+ \
+ static void \
+ tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \
+ int w, int h) { \
+ uint##bpp##_t pix, diff; \
+ bool endian; \
+ int *prev; \
+ int max[3], shift[3]; \
+ int here[3], upper[3], left[3], upperleft[3]; \
+ int prediction; \
+ int x, y, c; \
+ \
+ memset (vs->tight_gradient.buffer, 0, w * 3 * sizeof(int)); \
+ \
+ endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \
+ \
+ max[0] = vs->clientds.pf.rmax; \
+ max[1] = vs->clientds.pf.gmax; \
+ max[2] = vs->clientds.pf.bmax; \
+ shift[0] = vs->clientds.pf.rshift; \
+ shift[1] = vs->clientds.pf.gshift; \
+ shift[2] = vs->clientds.pf.bshift; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (c = 0; c < 3; c++) { \
+ upper[c] = 0; \
+ here[c] = 0; \
+ } \
+ prev = (int *)vs->tight_gradient.buffer; \
+ for (x = 0; x < w; x++) { \
+ pix = *buf; \
+ if (endian) { \
+ pix = bswap_##bpp(pix); \
+ } \
+ diff = 0; \
+ for (c = 0; c < 3; c++) { \
+ upperleft[c] = upper[c]; \
+ left[c] = here[c]; \
+ upper[c] = *prev; \
+ here[c] = (int)(pix >> shift[c] & max[c]); \
+ *prev++ = here[c]; \
+ \
+ prediction = left[c] + upper[c] - upperleft[c]; \
+ if (prediction < 0) { \
+ prediction = 0; \
+ } else if (prediction > max[c]) { \
+ prediction = max[c]; \
+ } \
+ diff |= ((here[c] - prediction) & max[c]) \
+ << shift[c]; \
+ } \
+ if (endian) { \
+ diff = bswap_##bpp(diff); \
+ } \
+ *buf++ = diff; \
+ } \
+ } \
+ }
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+/*
+ * Check if a rectangle is all of the same color. If needSameColor is
+ * set to non-zero, then also check that its color equals to the
+ * *colorPtr value. The result is 1 if the test is successfull, and in
+ * that case new color will be stored in *colorPtr.
+ */
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+ static bool \
+ check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \
+ uint32_t* color, bool samecolor) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t c; \
+ int dx, dy; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ c = *fbptr; \
+ if (samecolor && (uint32_t)c != *color) { \
+ return false; \
+ } \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (c != fbptr[dx]) { \
+ return false; \
+ } \
+ } \
+ fbptr = (uint##bpp##_t *) \
+ ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \
+ } \
+ \
+ *color = (uint32_t)c; \
+ return true; \
+ }
+
+DEFINE_CHECK_SOLID_FUNCTION(32)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(8)
+
+static bool check_solid_tile(VncState *vs, int x, int y, int w, int h,
+ uint32_t* color, bool samecolor)
+{
+ VncDisplay *vd = vs->vd;
+
+ switch(vd->server->pf.bytes_per_pixel) {
+ case 4:
+ return check_solid_tile32(vs, x, y, w, h, color, samecolor);
+ case 2:
+ return check_solid_tile16(vs, x, y, w, h, color, samecolor);
+ default:
+ return check_solid_tile8(vs, x, y, w, h, color, samecolor);
+ }
+}
+
+static void find_best_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *w_ptr, int *h_ptr)
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy);
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev);
+
+ if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) {
+ break;
+ }
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx);
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) {
+ break;
+ }
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+static void extend_solid_area(VncState *vs, int x, int y, int w, int h,
+ uint32_t color, int *x_ptr, int *y_ptr,
+ int *w_ptr, int *h_ptr)
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static int tight_init_stream(VncState *vs, int stream_id,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight_stream[stream_id];
+
+ if (zstream->opaque == NULL) {
+ int err;
+
+ VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id);
+ VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, strategy);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->tight_levels[stream_id] = level;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight_levels[stream_id] != level) {
+ if (deflateParams(zstream, level, strategy) != Z_OK) {
+ return -1;
+ }
+ vs->tight_levels[stream_id] = level;
+ }
+ return 0;
+}
+
+static void tight_send_compact_size(VncState *vs, size_t len)
+{
+ int lpc = 0;
+ int bytes = 0;
+ char buf[3] = {0, 0, 0};
+
+ buf[bytes++] = len & 0x7F;
+ if (len > 0x7F) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 7) & 0x7F;
+ if (len > 0x3FFF) {
+ buf[bytes-1] |= 0x80;
+ buf[bytes++] = (len >> 14) & 0xFF;
+ }
+ }
+ for (lpc = 0; lpc < bytes; lpc++) {
+ vnc_write_u8(vs, buf[lpc]);
+ }
+}
+
+static int tight_compress_data(VncState *vs, int stream_id, size_t bytes,
+ int level, int strategy)
+{
+ z_streamp zstream = &vs->tight_stream[stream_id];
+ int previous_out;
+
+ if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) {
+ vnc_write(vs, vs->tight.buffer, vs->tight.offset);
+ return bytes;
+ }
+
+ if (tight_init_stream(vs, stream_id, level, strategy)) {
+ return -1;
+ }
+
+ /* reserve memory in output buffer */
+ buffer_reserve(&vs->tight_zlib, bytes + 64);
+
+ /* set pointers */
+ zstream->next_in = vs->tight.buffer;
+ zstream->avail_in = vs->tight.offset;
+ zstream->next_out = vs->tight_zlib.buffer + vs->tight_zlib.offset;
+ zstream->avail_out = vs->tight_zlib.capacity - vs->tight_zlib.offset;
+ zstream->data_type = Z_BINARY;
+ previous_out = zstream->total_out;
+
+ /* start encoding */
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during tight compression\n");
+ return -1;
+ }
+
+ vs->tight_zlib.offset = vs->tight_zlib.capacity - zstream->avail_out;
+ bytes = zstream->total_out - previous_out;
+
+ tight_send_compact_size(vs, bytes);
+ vnc_write(vs, vs->tight_zlib.buffer, bytes);
+
+ buffer_reset(&vs->tight_zlib);
+
+ return bytes;
+}
+
+/*
+ * Subencoding implementations.
+ */
+static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret)
+{
+ uint32_t *buf32;
+ uint32_t pix;
+ int rshift, gshift, bshift;
+
+ buf32 = (uint32_t *)buf;
+
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) {
+ rshift = vs->clientds.pf.rshift;
+ gshift = vs->clientds.pf.gshift;
+ bshift = vs->clientds.pf.bshift;
+ } else {
+ rshift = 24 - vs->clientds.pf.rshift;
+ gshift = 24 - vs->clientds.pf.gshift;
+ bshift = 24 - vs->clientds.pf.bshift;
+ }
+
+ if (ret) {
+ *ret = count * 3;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> rshift);
+ *buf++ = (char)(pix >> gshift);
+ *buf++ = (char)(pix >> bshift);
+ }
+}
+
+static int send_full_color_rect(VncState *vs, int w, int h)
+{
+ int stream = 0;
+ size_t bytes;
+
+ vnc_write_u8(vs, stream << 4); /* no flushing, no filter */
+
+ if (vs->tight_pixel24) {
+ tight_pack24(vs, vs->tight.buffer, w * h, &vs->tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ bytes = tight_compress_data(vs, stream, w * h * bytes,
+ tight_conf[vs->tight_compression].raw_zlib_level,
+ Z_DEFAULT_STRATEGY);
+
+ return (bytes >= 0);
+}
+
+static int send_solid_rect(VncState *vs)
+{
+ size_t bytes;
+
+ vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */
+
+ if (vs->tight_pixel24) {
+ tight_pack24(vs, vs->tight.buffer, 1, &vs->tight.offset);
+ bytes = 3;
+ } else {
+ bytes = vs->clientds.pf.bytes_per_pixel;
+ }
+
+ vnc_write(vs, vs->tight.buffer, bytes);
+ return 1;
+}
+
+static int send_mono_rect(VncState *vs, int w, int h, uint32_t bg, uint32_t fg)
+{
+ size_t bytes;
+ int stream = 1;
+ int level = tight_conf[vs->tight_compression].mono_zlib_level;
+
+ bytes = ((w + 7) / 8) * h;
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ uint32_t buf[2] = {bg, fg};
+ size_t ret = sizeof (buf);
+
+ if (vs->tight_pixel24) {
+ tight_pack24(vs, (unsigned char*)buf, 2, &ret);
+ }
+ vnc_write(vs, buf, ret);
+
+ tight_encode_mono_rect32(vs->tight.buffer, w, h, bg, fg);
+ break;
+ }
+ case 2:
+ vnc_write(vs, &bg, 2);
+ vnc_write(vs, &fg, 2);
+ tight_encode_mono_rect16(vs->tight.buffer, w, h, bg, fg);
+ break;
+ default:
+ vnc_write_u8(vs, bg);
+ vnc_write_u8(vs, fg);
+ tight_encode_mono_rect8(vs->tight.buffer, w, h, bg, fg);
+ break;
+ }
+ vs->tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+struct palette_cb_priv {
+ VncState *vs;
+ uint8_t *header;
+};
+
+static void write_palette(const char *key, QObject *obj, void *opaque)
+{
+ struct palette_cb_priv *priv = opaque;
+ VncState *vs = priv->vs;
+ uint32_t bytes = vs->clientds.pf.bytes_per_pixel;
+ uint8_t idx = qint_get_int(qobject_to_qint(obj));
+
+ if (bytes == 4) {
+ uint32_t color = tight_palette_buf2rgb(32, (uint8_t *)key);
+
+ ((uint32_t*)priv->header)[idx] = color;
+ } else {
+ uint16_t color = tight_palette_buf2rgb(16, (uint8_t *)key);
+
+ ((uint16_t*)priv->header)[idx] = color;
+ }
+}
+
+static bool send_gradient_rect(VncState *vs, int w, int h)
+{
+ int stream = 3;
+ int level = tight_conf[vs->tight_compression].gradient_zlib_level;
+ size_t bytes;
+
+ if (vs->clientds.pf.bytes_per_pixel == 1)
+ return send_full_color_rect(vs, w, h);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT);
+
+ buffer_reserve(&vs->tight_gradient, w * 3 * sizeof (int));
+
+ if (vs->tight_pixel24) {
+ tight_filter_gradient24(vs, vs->tight.buffer, w, h);
+ bytes = 3;
+ } else if (vs->clientds.pf.bytes_per_pixel == 4) {
+ tight_filter_gradient32(vs, (uint32_t *)vs->tight.buffer, w, h);
+ bytes = 4;
+ } else {
+ tight_filter_gradient16(vs, (uint16_t *)vs->tight.buffer, w, h);
+ bytes = 2;
+ }
+
+ buffer_reset(&vs->tight_gradient);
+
+ bytes = w * h * bytes;
+ vs->tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_FILTERED);
+ return (bytes >= 0);
+}
+
+static int send_palette_rect(VncState *vs, int w, int h, struct QDict *palette)
+{
+ int stream = 2;
+ int level = tight_conf[vs->tight_compression].idx_zlib_level;
+ int colors;
+ size_t bytes;
+
+ colors = qdict_size(palette);
+
+ vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4);
+ vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE);
+ vnc_write_u8(vs, colors - 1);
+
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 4:
+ {
+ size_t old_offset, offset;
+ uint32_t header[qdict_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ old_offset = vs->output.offset;
+ qdict_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+
+ if (vs->tight_pixel24) {
+ tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset);
+ vs->output.offset = old_offset + offset;
+ }
+
+ tight_encode_indexed_rect32(vs->tight.buffer, w * h, palette);
+ break;
+ }
+ case 2:
+ {
+ uint16_t header[qdict_size(palette)];
+ struct palette_cb_priv priv = { vs, (uint8_t *)header };
+
+ qdict_iter(palette, write_palette, &priv);
+ vnc_write(vs, header, sizeof(header));
+ tight_encode_indexed_rect16(vs->tight.buffer, w * h, palette);
+ break;
+ }
+ default:
+ return -1; /* No palette for 8bits colors */
+ break;
+ }
+ bytes = w * h;
+ vs->tight.offset = bytes;
+
+ bytes = tight_compress_data(vs, stream, bytes,
+ level, Z_DEFAULT_STRATEGY);
+ return (bytes >= 0);
+}
+
+/*
+ * JPEG compression stuff.
+ */
+#ifdef CONFIG_VNC_JPEG
+static void jpeg_prepare_row24(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ VncDisplay *vd = vs->vd;
+ uint32_t *fbptr;
+ uint32_t pix;
+
+ fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) +
+ x * ds_get_bytes_per_pixel(vs->ds));
+
+ while (count--) {
+ pix = *fbptr++;
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift);
+ *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift);
+ }
+}
+
+#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
+ \
+ static void \
+ jpeg_prepare_row##bpp(VncState *vs, uint8_t *dst, \
+ int x, int y, int count) \
+ { \
+ VncDisplay *vd = vs->vd; \
+ uint##bpp##_t *fbptr; \
+ uint##bpp##_t pix; \
+ int r, g, b; \
+ \
+ fbptr = (uint##bpp##_t *) \
+ (vd->server->data + y * ds_get_linesize(vs->ds) + \
+ x * ds_get_bytes_per_pixel(vs->ds)); \
+ \
+ while (count--) { \
+ pix = *fbptr++; \
+ \
+ r = (int)((pix >> vs->ds->surface->pf.rshift) \
+ & vs->ds->surface->pf.rmax); \
+ g = (int)((pix >> vs->ds->surface->pf.gshift) \
+ & vs->ds->surface->pf.gmax); \
+ b = (int)((pix >> vs->ds->surface->pf.bshift) \
+ & vs->ds->surface->pf.bmax); \
+ \
+ *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \
+ / vs->ds->surface->pf.rmax); \
+ *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \
+ / vs->ds->surface->pf.gmax); \
+ *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \
+ / vs->ds->surface->pf.bmax); \
+ } \
+ }
+
+DEFINE_JPEG_GET_ROW_FUNCTION(16)
+DEFINE_JPEG_GET_ROW_FUNCTION(32)
+
+static void jpeg_prepare_row(VncState *vs, uint8_t *dst, int x, int y,
+ int count)
+{
+ if (vs->tight_pixel24)
+ jpeg_prepare_row24(vs, dst, x, y, count);
+ else if (ds_get_bytes_per_pixel(vs->ds) == 4)
+ jpeg_prepare_row32(vs, dst, x, y, count);
+ else
+ jpeg_prepare_row16(vs, dst, x, y, count);
+}
+
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+/* This is called once per encoding */
+static void jpeg_init_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight_jpeg;
+
+ cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset;
+ cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset);
+}
+
+/* This is called when we ran out of buffer (shouldn't happen!) */
+static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight_jpeg;
+
+ buffer->offset = buffer->capacity;
+ buffer_reserve(buffer, 2048);
+ jpeg_init_destination(cinfo);
+ return TRUE;
+}
+
+/* This is called when we are done processing data */
+static void jpeg_term_destination(j_compress_ptr cinfo)
+{
+ VncState *vs = cinfo->client_data;
+ Buffer *buffer = &vs->tight_jpeg;
+
+ buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer;
+}
+
+static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ struct jpeg_destination_mgr manager;
+ JSAMPROW row[1];
+ uint8_t *buf;
+ int dy;
+
+ if (ds_get_bytes_per_pixel(vs->ds) == 1)
+ return send_full_color_rect(vs, w, h);
+
+ buf = qemu_malloc(w * 3);
+ row[0] = buf;
+ buffer_reserve(&vs->tight_jpeg, 2048);
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.client_data = vs;
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, true);
+
+ manager.init_destination = jpeg_init_destination;
+ manager.empty_output_buffer = jpeg_empty_output_buffer;
+ manager.term_destination = jpeg_term_destination;
+ cinfo.dest = &manager;
+
+ jpeg_start_compress(&cinfo, true);
+
+ for (dy = 0; dy < h; dy++) {
+ jpeg_prepare_row(vs, buf, x, y + dy, w);
+ jpeg_write_scanlines(&cinfo, row, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ vnc_write_u8(vs, VNC_TIGHT_JPEG << 4);
+
+ tight_send_compact_size(vs, vs->tight_jpeg.offset);
+ vnc_write(vs, vs->tight_jpeg.buffer, vs->tight_jpeg.offset);
+ buffer_reset(&vs->tight_jpeg);
+
+ return 1;
+}
+#endif /* CONFIG_VNC_JPEG */
+
+static void vnc_tight_start(VncState *vs)
+{
+ buffer_reset(&vs->tight);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->tight_tmp = vs->output;
+ vs->output = vs->tight;
+}
+
+static void vnc_tight_stop(VncState *vs)
+{
+ // switch back to normal output/zlib buffers
+ vs->tight = vs->output;
+ vs->output = vs->tight_tmp;
+}
+
+static int send_sub_rect(VncState *vs, int x, int y, int w, int h)
+{
+ struct QDict *palette = NULL;
+ uint32_t bg = 0, fg = 0;
+ int colors;
+ int ret = 0;
+
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette);
+
+ if (colors == 0) {
+ if (tight_detect_smooth_image(vs, w, h)) {
+ if (vs->tight_quality == -1) {
+ ret = send_gradient_rect(vs, w, h);
+ } else {
+#ifdef CONFIG_VNC_JPEG
+ int quality = tight_conf[vs->tight_quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+#else
+ ret = send_full_color_rect(vs, w, h);
+#endif
+ }
+ } else {
+ ret = send_full_color_rect(vs, w, h);
+ }
+ } else if (colors == 1) {
+ ret = send_solid_rect(vs);
+ } else if (colors == 2) {
+ ret = send_mono_rect(vs, w, h, bg, fg);
+ } else if (colors <= 256) {
+#ifdef CONFIG_VNC_JPEG
+ if (colors > 96 && vs->tight_quality != -1 && vs->tight_quality <= 3 &&
+ tight_detect_smooth_image(vs, w, h)) {
+ int quality = tight_conf[vs->tight_quality].jpeg_quality;
+
+ ret = send_jpeg_rect(vs, x, y, w, h, quality);
+ } else {
+ ret = send_palette_rect(vs, w, h, palette);
+ }
+#else
+ ret = send_palette_rect(vs, w, h, palette);
+#endif
+ }
+ QDECREF(palette);
+ return ret;
+}
+
+static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h)
+{
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_TIGHT);
+
+ vnc_tight_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ vnc_tight_stop(vs);
+
+ return send_solid_rect(vs);
+}
+
+static int send_rect_simple(VncState *vs, int x, int y, int w, int h)
+{
+ int max_size, max_width;
+ int max_sub_width, max_sub_height;
+ int dx, dy;
+ int rw, rh;
+ int n = 0;
+
+ max_size = tight_conf[vs->tight_compression].max_rect_size;
+ max_width = tight_conf[vs->tight_compression].max_rect_width;
+
+ if (w > max_width || w * h > max_size) {
+ max_sub_width = (w > max_width) ? max_width : w;
+ max_sub_height = max_size / max_sub_width;
+
+ for (dy = 0; dy < h; dy += max_sub_height) {
+ for (dx = 0; dx < w; dx += max_width) {
+ rw = MIN(max_sub_width, w - dx);
+ rh = MIN(max_sub_height, h - dy);
+ n += send_sub_rect(vs, x+dx, y+dy, rw, rh);
+ }
+ }
+ } else {
+ n += send_sub_rect(vs, x, y, w, h);
+ }
+
+ return n;
+}
+
+static int find_large_solid_color_rect(VncState *vs, int x, int y,
+ int w, int h, int max_rows)
+{
+ int dx, dy, dw, dh;
+ int n = 0;
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= max_rows) {
+ n += send_rect_simple(vs, x, y, w, max_rows);
+ y += max_rows;
+ h -= max_rows;
+ }
+
+ dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy));
+
+ for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) {
+ uint32_t color_value;
+ int x_best, y_best, w_best, h_best;
+
+ dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx));
+
+ if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) {
+ continue ;
+ }
+
+ /* Get dimensions of solid-color area. */
+
+ find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y),
+ color_value, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if (w_best * h_best != w * h &&
+ w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) {
+ continue;
+ }
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ extend_solid_area(vs, x, y, w, h, color_value,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if (y_best != y) {
+ n += send_rect_simple(vs, x, y, w, y_best-y);
+ }
+ if (x_best != x) {
+ n += vnc_tight_send_framebuffer_update(vs, x, y_best,
+ x_best-x, h_best);
+ }
+
+ /* Send solid-color rectangle. */
+ n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best);
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if (x_best + w_best != x + w) {
+ n += vnc_tight_send_framebuffer_update(vs, x_best+w_best,
+ y_best,
+ w-(x_best-x)-w_best,
+ h_best);
+ }
+ if (y_best + h_best != y + h) {
+ n += vnc_tight_send_framebuffer_update(vs, x, y_best+h_best,
+ w, h-(y_best-y)-h_best);
+ }
+
+ /* Return after all recursive calls are done. */
+ return n;
+ }
+ }
+ return n + send_rect_simple(vs, x, y, w, h);
+}
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y,
+ int w, int h)
+{
+ int max_rows;
+
+ if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF &&
+ vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) {
+ vs->tight_pixel24 = true;
+ } else {
+ vs->tight_pixel24 = false;
+ }
+
+ if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE)
+ return send_rect_simple(vs, x, y, w, h);
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ max_rows = tight_conf[vs->tight_compression].max_rect_size;
+ max_rows /= MIN(tight_conf[vs->tight_compression].max_rect_width, w);
+
+ return find_large_solid_color_rect(vs, x, y, w, h, max_rows);
+}
+
+void vnc_tight_clear(VncState *vs)
+{
+ int i;
+ for (i=0; i<ARRAY_SIZE(vs->tight_stream); i++) {
+ if (vs->tight_stream[i].opaque) {
+ deflateEnd(&vs->tight_stream[i]);
+ }
+ }
+
+ buffer_free(&vs->tight);
+ buffer_free(&vs->tight_zlib);
+ buffer_free(&vs->tight_gradient);
+#ifdef CONFIG_VNC_JPEG
+ buffer_free(&vs->tight_jpeg);
+#endif
+}
diff --git a/ui/vnc-encoding-tight.h b/ui/vnc-encoding-tight.h
new file mode 100644
index 0000000000..9b0910c79a
--- /dev/null
+++ b/ui/vnc-encoding-tight.h
@@ -0,0 +1,181 @@
+/*
+ * QEMU VNC display driver: tight encoding
+ *
+ * From libvncserver/rfb/rfbproto.h
+ * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ *
+ * 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 VNC_ENCODING_TIGHT_H
+#define VNC_ENCODING_TIGHT_H
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Tight Encoding.
+ *
+ *-- The first byte of each Tight-encoded rectangle is a "compression control
+ * byte". Its format is as follows (bit 0 is the least significant one):
+ *
+ * bit 0: if 1, then compression stream 0 should be reset;
+ * bit 1: if 1, then compression stream 1 should be reset;
+ * bit 2: if 1, then compression stream 2 should be reset;
+ * bit 3: if 1, then compression stream 3 should be reset;
+ * bits 7-4: if 1000 (0x08), then the compression type is "fill",
+ * if 1001 (0x09), then the compression type is "jpeg",
+ * if 0xxx, then the compression type is "basic",
+ * values greater than 1001 are not valid.
+ *
+ * If the compression type is "basic", then bits 6..4 of the
+ * compression control byte (those xxx in 0xxx) specify the following:
+ *
+ * bits 5-4: decimal representation is the index of a particular zlib
+ * stream which should be used for decompressing the data;
+ * bit 6: if 1, then a "filter id" byte is following this byte.
+ *
+ *-- The data that follows after the compression control byte described
+ * above depends on the compression type ("fill", "jpeg" or "basic").
+ *
+ *-- If the compression type is "fill", then the only pixel value follows, in
+ * client pixel format (see NOTE 1). This value applies to all pixels of the
+ * rectangle.
+ *
+ *-- If the compression type is "jpeg", the following data stream looks like
+ * this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: JPEG image.
+ *
+ * Data size is compactly represented in one, two or three bytes, according
+ * to the following scheme:
+ *
+ * 0xxxxxxx (for values 0..127)
+ * 1xxxxxxx 0yyyyyyy (for values 128..16383)
+ * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303)
+ *
+ * Here each character denotes one bit, xxxxxxx are the least significant 7
+ * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the
+ * most significant 8 bits (bits 14-21). For example, decimal value 10000
+ * should be represented as two bytes: binary 10010000 01001110, or
+ * hexadecimal 90 4E.
+ *
+ *-- If the compression type is "basic" and bit 6 of the compression control
+ * byte was set to 1, then the next (second) byte specifies "filter id" which
+ * tells the decoder what filter type was used by the encoder to pre-process
+ * pixel data before the compression. The "filter id" byte can be one of the
+ * following:
+ *
+ * 0: no filter ("copy" filter);
+ * 1: "palette" filter;
+ * 2: "gradient" filter.
+ *
+ *-- If bit 6 of the compression control byte is set to 0 (no "filter id"
+ * byte), or if the filter id is 0, then raw pixel values in the client
+ * format (see NOTE 1) will be compressed. See below details on the
+ * compression.
+ *
+ *-- The "gradient" filter pre-processes pixel data with a simple algorithm
+ * which converts each color component to a difference between a "predicted"
+ * intensity and the actual intensity. Such a technique does not affect
+ * uncompressed data size, but helps to compress photo-like images better.
+ * Pseudo-code for converting intensities to differences is the following:
+ *
+ * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
+ * if (P[i,j] < 0) then P[i,j] := 0;
+ * if (P[i,j] > MAX) then P[i,j] := MAX;
+ * D[i,j] := V[i,j] - P[i,j];
+ *
+ * Here V[i,j] is the intensity of a color component for a pixel at
+ * coordinates (i,j). MAX is the maximum value of intensity for a color
+ * component.
+ *
+ *-- The "palette" filter converts true-color pixel data to indexed colors
+ * and a palette which can consist of 2..256 colors. If the number of colors
+ * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to
+ * encode one pixel. 1-bit encoding is performed such way that the most
+ * significant bits correspond to the leftmost pixels, and each raw of pixels
+ * is aligned to the byte boundary. When "palette" filter is used, the
+ * palette is sent before the pixel data. The palette begins with an unsigned
+ * byte which value is the number of colors in the palette minus 1 (i.e. 1
+ * means 2 colors, 255 means 256 colors in the palette). Then follows the
+ * palette itself which consist of pixel values in client pixel format (see
+ * NOTE 1).
+ *
+ *-- The pixel data is compressed using the zlib library. But if the data
+ * size after applying the filter but before the compression is less then 12,
+ * then the data is sent as is, uncompressed. Four separate zlib streams
+ * (0..3) can be used and the decoder should read the actual stream id from
+ * the compression control byte (see NOTE 2).
+ *
+ * If the compression is not used, then the pixel data is sent as is,
+ * otherwise the data stream looks like this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: zlib-compressed data.
+ *
+ * Data size is compactly represented in one, two or three bytes, just like
+ * in the "jpeg" compression method (see above).
+ *
+ *-- NOTE 1. If the color depth is 24, and all three color components are
+ * 8-bit wide, then one pixel in Tight encoding is always represented by
+ * three bytes, where the first byte is red component, the second byte is
+ * green component, and the third byte is blue component of the pixel color
+ * value. This applies to colors in palettes as well.
+ *
+ *-- NOTE 2. The decoder must reset compression streams' states before
+ * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
+ * byte are set to 1. Note that the decoder must reset zlib streams even if
+ * the compression type is "fill" or "jpeg".
+ *
+ *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
+ * when bits-per-pixel value is either 16 or 32, not 8.
+ *
+ *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048
+ * pixels. If a rectangle is wider, it must be split into several rectangles
+ * and each one should be encoded separately.
+ *
+ */
+
+#define VNC_TIGHT_EXPLICIT_FILTER 0x04
+#define VNC_TIGHT_FILL 0x08
+#define VNC_TIGHT_JPEG 0x09
+#define VNC_TIGHT_MAX_SUBENCODING 0x09
+
+/* Filters to improve compression efficiency */
+#define VNC_TIGHT_FILTER_COPY 0x00
+#define VNC_TIGHT_FILTER_PALETTE 0x01
+#define VNC_TIGHT_FILTER_GRADIENT 0x02
+
+/* Note: The following constant should not be changed. */
+#define VNC_TIGHT_MIN_TO_COMPRESS 12
+
+/* The parameters below may be adjusted. */
+#define VNC_TIGHT_MIN_SPLIT_RECT_SIZE 4096
+#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048
+#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16
+
+#define VNC_TIGHT_JPEG_MIN_RECT_SIZE 4096
+#define VNC_TIGHT_DETECT_SUBROW_WIDTH 7
+#define VNC_TIGHT_DETECT_MIN_WIDTH 8
+#define VNC_TIGHT_DETECT_MIN_HEIGHT 8
+
+#endif /* VNC_ENCODING_TIGHT_H */
diff --git a/ui/vnc-encoding-zlib.c b/ui/vnc-encoding-zlib.c
new file mode 100644
index 0000000000..a99bc387dc
--- /dev/null
+++ b/ui/vnc-encoding-zlib.c
@@ -0,0 +1,152 @@
+/*
+ * QEMU VNC display driver: zlib encoding
+ *
+ * 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 "vnc.h"
+
+#define ZALLOC_ALIGNMENT 16
+
+void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size)
+{
+ void *p;
+
+ size *= items;
+ size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+ p = qemu_mallocz(size);
+
+ return (p);
+}
+
+void vnc_zlib_zfree(void *x, void *addr)
+{
+ qemu_free(addr);
+}
+
+static void vnc_zlib_start(VncState *vs)
+{
+ buffer_reset(&vs->zlib);
+
+ // make the output buffer be the zlib buffer, so we can compress it later
+ vs->zlib_tmp = vs->output;
+ vs->output = vs->zlib;
+}
+
+static int vnc_zlib_stop(VncState *vs)
+{
+ z_streamp zstream = &vs->zlib_stream;
+ int previous_out;
+
+ // switch back to normal output/zlib buffers
+ vs->zlib = vs->output;
+ vs->output = vs->zlib_tmp;
+
+ // compress the zlib buffer
+
+ // initialize the stream
+ // XXX need one stream per session
+ if (zstream->opaque != vs) {
+ int err;
+
+ VNC_DEBUG("VNC: initializing zlib stream\n");
+ VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs);
+ zstream->zalloc = vnc_zlib_zalloc;
+ zstream->zfree = vnc_zlib_zfree;
+
+ err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+
+ if (err != Z_OK) {
+ fprintf(stderr, "VNC: error initializing zlib\n");
+ return -1;
+ }
+
+ vs->zlib_level = vs->tight_compression;
+ zstream->opaque = vs;
+ }
+
+ if (vs->tight_compression != vs->zlib_level) {
+ if (deflateParams(zstream, vs->tight_compression,
+ Z_DEFAULT_STRATEGY) != Z_OK) {
+ return -1;
+ }
+ vs->zlib_level = vs->tight_compression;
+ }
+
+ // reserve memory in output buffer
+ buffer_reserve(&vs->output, vs->zlib.offset + 64);
+
+ // set pointers
+ zstream->next_in = vs->zlib.buffer;
+ zstream->avail_in = vs->zlib.offset;
+ zstream->next_out = vs->output.buffer + vs->output.offset;
+ zstream->avail_out = vs->output.capacity - vs->output.offset;
+ zstream->data_type = Z_BINARY;
+ previous_out = zstream->total_out;
+
+ // start encoding
+ if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) {
+ fprintf(stderr, "VNC: error during zlib compression\n");
+ return -1;
+ }
+
+ vs->output.offset = vs->output.capacity - zstream->avail_out;
+ return zstream->total_out - previous_out;
+}
+
+int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int old_offset, new_offset, bytes_written;
+
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB);
+
+ // remember where we put in the follow-up size
+ old_offset = vs->output.offset;
+ vnc_write_s32(vs, 0);
+
+ // compress the stream
+ vnc_zlib_start(vs);
+ vnc_raw_send_framebuffer_update(vs, x, y, w, h);
+ bytes_written = vnc_zlib_stop(vs);
+
+ if (bytes_written == -1)
+ return 0;
+
+ // hack in the size
+ new_offset = vs->output.offset;
+ vs->output.offset = old_offset;
+ vnc_write_u32(vs, bytes_written);
+ vs->output.offset = new_offset;
+
+ return 1;
+}
+
+void vnc_zlib_clear(VncState *vs)
+{
+ if (vs->zlib_stream.opaque) {
+ deflateEnd(&vs->zlib_stream);
+ }
+ buffer_free(&vs->zlib);
+}
diff --git a/ui/vnc-tls.c b/ui/vnc-tls.c
new file mode 100644
index 0000000000..dec626c539
--- /dev/null
+++ b/ui/vnc-tls.c
@@ -0,0 +1,445 @@
+/*
+ * 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_socket.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-hellmen 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) {
+ struct VncState *vs = (struct 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) {
+ struct VncState *vs = (struct VncState *)transport;
+ int ret;
+
+ retry:
+ ret = recv(vs->csock, data, len, 0);
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto retry;
+ return -1;
+ }
+ return ret;
+}
+
+
+static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
+{
+ gnutls_anon_server_credentials 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(struct 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 = qemu_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 = qemu_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;
+}
+
+
+int vnc_tls_client_setup(struct VncState *vs,
+ int needX509Creds) {
+ static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+ static const int protocol_priority[]= { 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};
+
+ 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 (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ vnc_client_error(vs);
+ return -1;
+ }
+
+ if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 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 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(struct VncState *vs)
+{
+ if (vs->tls.session) {
+ gnutls_deinit(vs->tls.session);
+ vs->tls.session = NULL;
+ }
+ vs->tls.wiremode = VNC_WIREMODE_CLEAR;
+ 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;
+
+ if (*cred) {
+ qemu_free(*cred);
+ *cred = NULL;
+ }
+
+ *cred = qemu_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) {
+ qemu_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:
+ qemu_free(vd->tls.x509cacert);
+ qemu_free(vd->tls.x509cacrl);
+ qemu_free(vd->tls.x509cert);
+ qemu_free(vd->tls.x509key);
+ vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL;
+ return -1;
+}
+
diff --git a/ui/vnc-tls.h b/ui/vnc-tls.h
new file mode 100644
index 0000000000..2b93633896
--- /dev/null
+++ b/ui/vnc-tls.h
@@ -0,0 +1,76 @@
+/*
+ * 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 "acl.h"
+
+enum {
+ VNC_WIREMODE_CLEAR,
+ VNC_WIREMODE_TLS,
+};
+
+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 {
+ /* Whether data is being TLS encrypted yet */
+ int wiremode;
+ 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/ui/vnc.c b/ui/vnc.c
new file mode 100644
index 0000000000..ccd7aad86b
--- /dev/null
+++ b/ui/vnc.c
@@ -0,0 +1,2631 @@
+/*
+ * QEMU VNC display driver
+ *
+ * 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 "vnc.h"
+#include "sysemu.h"
+#include "qemu_socket.h"
+#include "qemu-timer.h"
+#include "acl.h"
+#include "qemu-objects.h"
+
+#define VNC_REFRESH_INTERVAL_BASE 30
+#define VNC_REFRESH_INTERVAL_INC 50
+#define VNC_REFRESH_INTERVAL_MAX 2000
+
+#include "vnc_keysym.h"
+#include "d3des.h"
+
+#define count_bits(c, v) { \
+ for (c = 0; v; v >>= 1) \
+ { \
+ c += v & 1; \
+ } \
+}
+
+
+static VncDisplay *vnc_display; /* needed for info vnc */
+static DisplayChangeListener *dcl;
+
+static int vnc_cursor_define(VncState *vs);
+
+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 = qemu_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;
+
+ return addr_to_string(format, &sa, salen);
+}
+
+static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa,
+ socklen_t salen)
+{
+ 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 -1;
+ }
+
+ qdict_put(qdict, "host", qstring_from_str(host));
+ qdict_put(qdict, "service", qstring_from_str(serv));
+ qdict_put(qdict, "family",qstring_from_str(inet_strfamily(sa->ss_family)));
+
+ return 0;
+}
+
+static int vnc_server_addr_put(QDict *qdict, int fd)
+{
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) {
+ return -1;
+ }
+
+ return put_addr_qdict(qdict, &sa, salen);
+}
+
+static int vnc_qdict_remote_addr(QDict *qdict, int fd)
+{
+ struct sockaddr_storage sa;
+ socklen_t salen;
+
+ salen = sizeof(sa);
+ if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) {
+ return -1;
+ }
+
+ return put_addr_qdict(qdict, &sa, salen);
+}
+
+static const char *vnc_auth_name(VncDisplay *vd) {
+ switch (vd->auth) {
+ case VNC_AUTH_INVALID:
+ return "invalid";
+ case VNC_AUTH_NONE:
+ return "none";
+ case VNC_AUTH_VNC:
+ return "vnc";
+ case VNC_AUTH_RA2:
+ return "ra2";
+ case VNC_AUTH_RA2NE:
+ return "ra2ne";
+ case VNC_AUTH_TIGHT:
+ return "tight";
+ case VNC_AUTH_ULTRA:
+ return "ultra";
+ 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";
+ case VNC_AUTH_VENCRYPT_TLSNONE:
+ return "vencrypt+tls+none";
+ case VNC_AUTH_VENCRYPT_TLSVNC:
+ return "vencrypt+tls+vnc";
+ case VNC_AUTH_VENCRYPT_TLSPLAIN:
+ return "vencrypt+tls+plain";
+ case VNC_AUTH_VENCRYPT_X509NONE:
+ return "vencrypt+x509+none";
+ case VNC_AUTH_VENCRYPT_X509VNC:
+ return "vencrypt+x509+vnc";
+ case VNC_AUTH_VENCRYPT_X509PLAIN:
+ return "vencrypt+x509+plain";
+ case VNC_AUTH_VENCRYPT_TLSSASL:
+ return "vencrypt+tls+sasl";
+ case VNC_AUTH_VENCRYPT_X509SASL:
+ return "vencrypt+x509+sasl";
+ default:
+ return "vencrypt";
+ }
+#else
+ return "vencrypt";
+#endif
+ case VNC_AUTH_SASL:
+ return "sasl";
+ }
+ return "unknown";
+}
+
+static int vnc_server_info_put(QDict *qdict)
+{
+ if (vnc_server_addr_put(qdict, vnc_display->lsock) < 0) {
+ return -1;
+ }
+
+ qdict_put(qdict, "auth", qstring_from_str(vnc_auth_name(vnc_display)));
+ return 0;
+}
+
+static void vnc_client_cache_auth(VncState *client)
+{
+ QDict *qdict;
+
+ if (!client->info) {
+ return;
+ }
+
+ qdict = qobject_to_qdict(client->info);
+
+#ifdef CONFIG_VNC_TLS
+ if (client->tls.session &&
+ client->tls.dname) {
+ qdict_put(qdict, "x509_dname", qstring_from_str(client->tls.dname));
+ }
+#endif
+#ifdef CONFIG_VNC_SASL
+ if (client->sasl.conn &&
+ client->sasl.username) {
+ qdict_put(qdict, "sasl_username",
+ qstring_from_str(client->sasl.username));
+ }
+#endif
+}
+
+static void vnc_client_cache_addr(VncState *client)
+{
+ QDict *qdict;
+
+ qdict = qdict_new();
+ if (vnc_qdict_remote_addr(qdict, client->csock) < 0) {
+ QDECREF(qdict);
+ /* XXX: how to report the error? */
+ return;
+ }
+
+ client->info = QOBJECT(qdict);
+}
+
+static void vnc_qmp_event(VncState *vs, MonitorEvent event)
+{
+ QDict *server;
+ QObject *data;
+
+ if (!vs->info) {
+ return;
+ }
+
+ server = qdict_new();
+ if (vnc_server_info_put(server) < 0) {
+ QDECREF(server);
+ return;
+ }
+
+ data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
+ vs->info, QOBJECT(server));
+
+ monitor_protocol_event(event, data);
+
+ qobject_incref(vs->info);
+ qobject_decref(data);
+}
+
+static void info_vnc_iter(QObject *obj, void *opaque)
+{
+ QDict *client;
+ Monitor *mon = opaque;
+
+ client = qobject_to_qdict(obj);
+ monitor_printf(mon, "Client:\n");
+ monitor_printf(mon, " address: %s:%s\n",
+ qdict_get_str(client, "host"),
+ qdict_get_str(client, "service"));
+
+#ifdef CONFIG_VNC_TLS
+ monitor_printf(mon, " x509_dname: %s\n",
+ qdict_haskey(client, "x509_dname") ?
+ qdict_get_str(client, "x509_dname") : "none");
+#endif
+#ifdef CONFIG_VNC_SASL
+ monitor_printf(mon, " username: %s\n",
+ qdict_haskey(client, "sasl_username") ?
+ qdict_get_str(client, "sasl_username") : "none");
+#endif
+}
+
+void do_info_vnc_print(Monitor *mon, const QObject *data)
+{
+ QDict *server;
+ QList *clients;
+
+ server = qobject_to_qdict(data);
+ if (qdict_get_bool(server, "enabled") == 0) {
+ monitor_printf(mon, "Server: disabled\n");
+ return;
+ }
+
+ monitor_printf(mon, "Server:\n");
+ monitor_printf(mon, " address: %s:%s\n",
+ qdict_get_str(server, "host"),
+ qdict_get_str(server, "service"));
+ monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
+
+ clients = qdict_get_qlist(server, "clients");
+ if (qlist_empty(clients)) {
+ monitor_printf(mon, "Client: none\n");
+ } else {
+ qlist_iter(clients, info_vnc_iter, mon);
+ }
+}
+
+void do_info_vnc(Monitor *mon, QObject **ret_data)
+{
+ if (vnc_display == NULL || vnc_display->display == NULL) {
+ *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+ } else {
+ QList *clist;
+ VncState *client;
+
+ clist = qlist_new();
+ QTAILQ_FOREACH(client, &vnc_display->clients, next) {
+ if (client->info) {
+ /* incref so that it's not freed by upper layers */
+ qobject_incref(client->info);
+ qlist_append_obj(clist, client->info);
+ }
+ }
+
+ *ret_data = qobject_from_jsonf("{ 'enabled': true, 'clients': %p }",
+ QOBJECT(clist));
+ assert(*ret_data != NULL);
+
+ if (vnc_server_info_put(qobject_to_qdict(*ret_data)) < 0) {
+ qobject_decref(*ret_data);
+ *ret_data = NULL;
+ }
+ }
+}
+
+static inline uint32_t vnc_has_feature(VncState *vs, int feature) {
+ return (vs->features & (1 << feature));
+}
+
+/* TODO
+ 1) Get the queue working for IO.
+ 2) there is some weirdness when using the -S option (the screen is grey
+ and not totally invalidated
+ 3) resolutions > 1024
+*/
+
+static int vnc_update_client(VncState *vs, int has_dirty);
+static void vnc_disconnect_start(VncState *vs);
+static void vnc_disconnect_finish(VncState *vs);
+static void vnc_init_timer(VncDisplay *vd);
+static void vnc_remove_timer(VncDisplay *vd);
+
+static void vnc_colordepth(VncState *vs);
+static void framebuffer_update_request(VncState *vs, int incremental,
+ int x_position, int y_position,
+ int w, int h);
+static void vnc_refresh(void *opaque);
+static int vnc_refresh_server_surface(VncDisplay *vd);
+
+static inline void vnc_set_bit(uint32_t *d, int k)
+{
+ d[k >> 5] |= 1 << (k & 0x1f);
+}
+
+static inline void vnc_clear_bit(uint32_t *d, int k)
+{
+ d[k >> 5] &= ~(1 << (k & 0x1f));
+}
+
+static inline void vnc_set_bits(uint32_t *d, int n, int nb_words)
+{
+ int j;
+
+ j = 0;
+ while (n >= 32) {
+ d[j++] = -1;
+ n -= 32;
+ }
+ if (n > 0)
+ d[j++] = (1 << n) - 1;
+ while (j < nb_words)
+ d[j++] = 0;
+}
+
+static inline int vnc_get_bit(const uint32_t *d, int k)
+{
+ return (d[k >> 5] >> (k & 0x1f)) & 1;
+}
+
+static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
+ int nb_words)
+{
+ int i;
+ for(i = 0; i < nb_words; i++) {
+ if ((d1[i] & d2[i]) != 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ int i;
+ VncDisplay *vd = ds->opaque;
+ struct VncSurface *s = &vd->guest;
+
+ h += y;
+
+ /* round x down to ensure the loop only spans one 16-pixel block per,
+ iteration. otherwise, if (x % 16) != 0, the last iteration may span
+ two 16-pixel blocks but we only mark the first as dirty
+ */
+ w += (x % 16);
+ x -= (x % 16);
+
+ x = MIN(x, s->ds->width);
+ y = MIN(y, s->ds->height);
+ w = MIN(x + w, s->ds->width) - x;
+ h = MIN(h, s->ds->height);
+
+ for (; y < h; y++)
+ for (i = 0; i < w; i += 16)
+ vnc_set_bit(s->dirty[y], (x + i) / 16);
+}
+
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+ int32_t encoding)
+{
+ vnc_write_u16(vs, x);
+ vnc_write_u16(vs, y);
+ vnc_write_u16(vs, w);
+ vnc_write_u16(vs, 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 = qemu_realloc(buffer->buffer, buffer->capacity);
+ if (buffer->buffer == NULL) {
+ fprintf(stderr, "vnc: out of memory\n");
+ exit(1);
+ }
+ }
+}
+
+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)
+{
+ qemu_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;
+}
+
+static void vnc_desktop_resize(VncState *vs)
+{
+ DisplayState *ds = vs->ds;
+
+ if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) {
+ return;
+ }
+ if (vs->client_width == ds_get_width(ds) &&
+ vs->client_height == ds_get_height(ds)) {
+ return;
+ }
+ vs->client_width = ds_get_width(ds);
+ vs->client_height = ds_get_height(ds);
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height,
+ VNC_ENCODING_DESKTOPRESIZE);
+ vnc_flush(vs);
+}
+
+static void vnc_dpy_resize(DisplayState *ds)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs;
+
+ /* server surface */
+ if (!vd->server)
+ vd->server = qemu_mallocz(sizeof(*vd->server));
+ if (vd->server->data)
+ qemu_free(vd->server->data);
+ *(vd->server) = *(ds->surface);
+ vd->server->data = qemu_mallocz(vd->server->linesize *
+ vd->server->height);
+
+ /* guest surface */
+ if (!vd->guest.ds)
+ vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds));
+ if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel)
+ console_color_init(ds);
+ *(vd->guest.ds) = *(ds->surface);
+ memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty));
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_colordepth(vs);
+ vnc_desktop_resize(vs);
+ if (vs->vd->cursor) {
+ vnc_cursor_define(vs);
+ }
+ memset(vs->dirty, 0xFF, sizeof(vs->dirty));
+ }
+}
+
+/* fastest code */
+static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf,
+ void *pixels, int size)
+{
+ vnc_write(vs, pixels, size);
+}
+
+/* slowest but generic code. */
+void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
+{
+ uint8_t r, g, b;
+ VncDisplay *vd = vs->vd;
+
+ r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >>
+ vd->server->pf.rbits);
+ g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >>
+ vd->server->pf.gbits);
+ b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >>
+ vd->server->pf.bbits);
+ v = (r << vs->clientds.pf.rshift) |
+ (g << vs->clientds.pf.gshift) |
+ (b << vs->clientds.pf.bshift);
+ switch(vs->clientds.pf.bytes_per_pixel) {
+ case 1:
+ buf[0] = v;
+ break;
+ case 2:
+ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 8;
+ buf[1] = v;
+ } else {
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ default:
+ case 4:
+ if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) {
+ buf[0] = v >> 24;
+ buf[1] = v >> 16;
+ buf[2] = v >> 8;
+ buf[3] = v;
+ } else {
+ buf[3] = v >> 24;
+ buf[2] = v >> 16;
+ buf[1] = v >> 8;
+ buf[0] = v;
+ }
+ break;
+ }
+}
+
+static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf,
+ void *pixels1, int size)
+{
+ uint8_t buf[4];
+
+ if (pf->bytes_per_pixel == 4) {
+ uint32_t *pixels = pixels1;
+ int n, i;
+ n = size >> 2;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else if (pf->bytes_per_pixel == 2) {
+ uint16_t *pixels = pixels1;
+ int n, i;
+ n = size >> 1;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else if (pf->bytes_per_pixel == 1) {
+ uint8_t *pixels = pixels1;
+ int n, i;
+ n = size;
+ for(i = 0; i < n; i++) {
+ vnc_convert_pixel(vs, buf, pixels[i]);
+ vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel);
+ }
+ } else {
+ fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
+ }
+}
+
+int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int i;
+ uint8_t *row;
+ VncDisplay *vd = vs->vd;
+
+ row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ for (i = 0; i < h; i++) {
+ vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds));
+ row += ds_get_linesize(vs->ds);
+ }
+ return 1;
+}
+
+static int send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+ int n = 0;
+
+ switch(vs->vnc_encoding) {
+ case VNC_ENCODING_ZLIB:
+ n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_HEXTILE:
+ vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE);
+ n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h);
+ break;
+ case VNC_ENCODING_TIGHT:
+ n = vnc_tight_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);
+ break;
+ }
+ return n;
+}
+
+static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ /* send bitblit op to the vnc client */
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT);
+ vnc_write_u16(vs, src_x);
+ vnc_write_u16(vs, src_y);
+ vnc_flush(vs);
+}
+
+static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+ VncDisplay *vd = ds->opaque;
+ VncState *vs, *vn;
+ uint8_t *src_row;
+ uint8_t *dst_row;
+ int i,x,y,pitch,depth,inc,w_lim,s;
+ int cmp_bytes;
+
+ vnc_refresh_server_surface(vd);
+ QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vs->force_update = 1;
+ vnc_update_client(vs, 1);
+ /* vs might be free()ed here */
+ }
+ }
+
+ /* do bitblit op on the local surface too */
+ pitch = ds_get_linesize(vd->ds);
+ depth = ds_get_bytes_per_pixel(vd->ds);
+ src_row = vd->server->data + pitch * src_y + depth * src_x;
+ dst_row = vd->server->data + pitch * dst_y + depth * dst_x;
+ y = dst_y;
+ inc = 1;
+ if (dst_y > src_y) {
+ /* copy backwards */
+ src_row += pitch * (h-1);
+ dst_row += pitch * (h-1);
+ pitch = -pitch;
+ y = dst_y + h - 1;
+ inc = -1;
+ }
+ w_lim = w - (16 - (dst_x % 16));
+ if (w_lim < 0)
+ w_lim = w;
+ else
+ w_lim = w - (w_lim % 16);
+ for (i = 0; i < h; i++) {
+ for (x = 0; x <= w_lim;
+ x += s, src_row += cmp_bytes, dst_row += cmp_bytes) {
+ if (x == w_lim) {
+ if ((s = w - w_lim) == 0)
+ break;
+ } else if (!x) {
+ s = (16 - (dst_x % 16));
+ s = MIN(s, w_lim);
+ } else {
+ s = 16;
+ }
+ cmp_bytes = s * depth;
+ if (memcmp(src_row, dst_row, cmp_bytes) == 0)
+ continue;
+ memmove(dst_row, src_row, cmp_bytes);
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16));
+ }
+ }
+ }
+ src_row += pitch - w * depth;
+ dst_row += pitch - w * depth;
+ y += inc;
+ }
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) {
+ vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h);
+ }
+ }
+}
+
+static void vnc_mouse_set(int x, int y, int visible)
+{
+ /* can we ask the client(s) to move the pointer ??? */
+}
+
+static int vnc_cursor_define(VncState *vs)
+{
+ QEMUCursor *c = vs->vd->cursor;
+ PixelFormat pf = qemu_default_pixelformat(32);
+ int isize;
+
+ if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) {
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0); /* padding */
+ vnc_write_u16(vs, 1); /* # of rects */
+ vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height,
+ VNC_ENCODING_RICH_CURSOR);
+ isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel;
+ vnc_write_pixels_generic(vs, &pf, c->data, isize);
+ vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize);
+ return 0;
+ }
+ return -1;
+}
+
+static void vnc_dpy_cursor_define(QEMUCursor *c)
+{
+ VncDisplay *vd = vnc_display;
+ VncState *vs;
+
+ cursor_put(vd->cursor);
+ qemu_free(vd->cursor_mask);
+
+ vd->cursor = c;
+ cursor_get(vd->cursor);
+ vd->cursor_msize = cursor_get_mono_bpl(c) * c->height;
+ vd->cursor_mask = qemu_mallocz(vd->cursor_msize);
+ cursor_get_mono_mask(c, 0, vd->cursor_mask);
+
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_cursor_define(vs);
+ }
+}
+
+static int find_and_clear_dirty_height(struct VncState *vs,
+ int y, int last_x, int x)
+{
+ int h;
+ VncDisplay *vd = vs->vd;
+
+ for (h = 1; h < (vd->server->height - y); h++) {
+ int tmp_x;
+ if (!vnc_get_bit(vs->dirty[y + h], last_x))
+ break;
+ for (tmp_x = last_x; tmp_x < x; tmp_x++)
+ vnc_clear_bit(vs->dirty[y + h], tmp_x);
+ }
+
+ return h;
+}
+
+static int vnc_update_client(VncState *vs, int has_dirty)
+{
+ if (vs->need_update && vs->csock != -1) {
+ VncDisplay *vd = vs->vd;
+ int y;
+ int n_rectangles;
+ int saved_offset;
+ int width, height;
+ int n;
+
+ if (vs->output.offset && !vs->audio_cap && !vs->force_update)
+ /* kernel send buffers are full -> drop frames to throttle */
+ return 0;
+
+ if (!has_dirty && !vs->audio_cap && !vs->force_update)
+ return 0;
+
+ /*
+ * Send screen updates to the vnc client using the server
+ * surface and server dirty map. guest surface updates
+ * happening in parallel don't disturb us, the next pass will
+ * send them to the client.
+ */
+ n_rectangles = 0;
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ saved_offset = vs->output.offset;
+ vnc_write_u16(vs, 0);
+
+ width = MIN(vd->server->width, vs->client_width);
+ height = MIN(vd->server->height, vs->client_height);
+
+ for (y = 0; y < height; y++) {
+ int x;
+ int last_x = -1;
+ for (x = 0; x < width / 16; x++) {
+ if (vnc_get_bit(vs->dirty[y], x)) {
+ if (last_x == -1) {
+ last_x = x;
+ }
+ vnc_clear_bit(vs->dirty[y], x);
+ } else {
+ if (last_x != -1) {
+ int h = find_and_clear_dirty_height(vs, y, last_x, x);
+ n = send_framebuffer_update(vs, last_x * 16, y,
+ (x - last_x) * 16, h);
+ n_rectangles += n;
+ }
+ last_x = -1;
+ }
+ }
+ if (last_x != -1) {
+ int h = find_and_clear_dirty_height(vs, y, last_x, x);
+ n = send_framebuffer_update(vs, last_x * 16, y,
+ (x - last_x) * 16, h);
+ n_rectangles += n;
+ }
+ }
+ vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+ vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+ vnc_flush(vs);
+ vs->force_update = 0;
+ return n_rectangles;
+ }
+
+ if (vs->csock == -1)
+ vnc_disconnect_finish(vs);
+
+ return 0;
+}
+
+/* audio */
+static void audio_capture_notify(void *opaque, audcnotification_e cmd)
+{
+ VncState *vs = opaque;
+
+ switch (cmd) {
+ case AUD_CNOTIFY_DISABLE:
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END);
+ vnc_flush(vs);
+ break;
+
+ case AUD_CNOTIFY_ENABLE:
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN);
+ vnc_flush(vs);
+ break;
+ }
+}
+
+static void audio_capture_destroy(void *opaque)
+{
+}
+
+static void audio_capture(void *opaque, void *buf, int size)
+{
+ VncState *vs = opaque;
+
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU);
+ vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO);
+ vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA);
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
+ vnc_flush(vs);
+}
+
+static void audio_add(VncState *vs)
+{
+ struct audio_capture_ops ops;
+
+ if (vs->audio_cap) {
+ monitor_printf(default_mon, "audio already running\n");
+ return;
+ }
+
+ ops.notify = audio_capture_notify;
+ ops.destroy = audio_capture_destroy;
+ ops.capture = audio_capture;
+
+ vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
+ if (!vs->audio_cap) {
+ monitor_printf(default_mon, "Failed to add audio capture\n");
+ }
+}
+
+static void audio_del(VncState *vs)
+{
+ if (vs->audio_cap) {
+ AUD_del_capture(vs->audio_cap, vs);
+ vs->audio_cap = NULL;
+ }
+}
+
+static void vnc_disconnect_start(VncState *vs)
+{
+ if (vs->csock == -1)
+ return;
+ qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+ closesocket(vs->csock);
+ vs->csock = -1;
+}
+
+static void vnc_disconnect_finish(VncState *vs)
+{
+ vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED);
+
+ buffer_free(&vs->input);
+ buffer_free(&vs->output);
+
+ qobject_decref(vs->info);
+
+ vnc_zlib_clear(vs);
+ vnc_tight_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 */
+ audio_del(vs);
+
+ QTAILQ_REMOVE(&vs->vd->clients, vs, next);
+
+ if (QTAILQ_EMPTY(&vs->vd->clients)) {
+ dcl->idle = 1;
+ }
+
+ qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
+ vnc_remove_timer(vs->vd);
+ if (vs->vd->lock_key_sync)
+ qemu_remove_led_event_handler(vs->led);
+ qemu_free(vs);
+}
+
+int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+{
+ if (ret == 0 || ret == -1) {
+ if (ret == -1) {
+ switch (last_errno) {
+ case EINTR:
+ case EAGAIN:
+#ifdef _WIN32
+ case WSAEWOULDBLOCK:
+#endif
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ VNC_DEBUG("Closing down client sock: ret %d, errno %d\n",
+ ret, ret < 0 ? last_errno : 0);
+ vnc_disconnect_start(vs);
+
+ return 0;
+ }
+ return ret;
+}
+
+
+void vnc_client_error(VncState *vs)
+{
+ VNC_DEBUG("Closing down client sock: protocol error\n");
+ vnc_disconnect_start(vs);
+}
+
+
+/*
+ * Called to write a chunk of data to the client socket. The data may
+ * be the raw data, or may have already been encoded by SASL.
+ * The data will be written either straight onto the socket, or
+ * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes written, which may be less than
+ * 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)
+{
+ long ret;
+#ifdef CONFIG_VNC_TLS
+ if (vs->tls.session) {
+ ret = gnutls_write(vs->tls.session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EIO;
+ ret = -1;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ ret = send(vs->csock, (const void *)data, datalen, 0);
+ VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+
+
+/*
+ * Called to write buffered data to the client socket, when not
+ * using any SASL SSF encryption layers. Will write as much data
+ * as possible without blocking. If all buffered data is written,
+ * will switch the FD poll() handler back to read monitoring.
+ *
+ * Returns the number of bytes written, which may be less than
+ * 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)
+{
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n",
+ vs->output.buffer, vs->output.capacity, vs->output.offset,
+ vs->sasl.waitWriteSSF);
+
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ vs->sasl.waitWriteSSF) {
+ ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF);
+ if (ret)
+ vs->sasl.waitWriteSSF -= ret;
+ } else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset);
+ if (!ret)
+ return 0;
+
+ memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
+ vs->output.offset -= ret;
+
+ if (vs->output.offset == 0) {
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+ }
+
+ return ret;
+}
+
+
+/*
+ * First function called whenever there is data to be written to
+ * the client socket. Will delegate actual work according to whether
+ * SASL SSF layers are enabled (thus requiring encryption calls)
+ */
+void vnc_client_write(void *opaque)
+{
+ VncState *vs = opaque;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn &&
+ vs->sasl.runSSF &&
+ !vs->sasl.waitWriteSSF) {
+ vnc_client_write_sasl(vs);
+ } else
+#endif /* CONFIG_VNC_SASL */
+ vnc_client_write_plain(vs);
+}
+
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+{
+ vs->read_handler = func;
+ vs->read_handler_expect = expecting;
+}
+
+
+/*
+ * Called to read a chunk of data from the client socket. The data may
+ * be the raw data, or may need to be further decoded by SASL.
+ * The data will be read either straight from to the socket, or
+ * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled
+ *
+ * NB, it is theoretically possible to have 2 layers of encryption,
+ * both SASL, and this TLS layer. It is highly unlikely in practice
+ * though, since SASL encryption will typically be a no-op if TLS
+ * is active
+ *
+ * Returns the number of bytes read, which may be less than
+ * 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)
+{
+ long ret;
+#ifdef CONFIG_VNC_TLS
+ if (vs->tls.session) {
+ ret = gnutls_read(vs->tls.session, data, datalen);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_AGAIN)
+ errno = EAGAIN;
+ else
+ errno = EIO;
+ ret = -1;
+ }
+ } else
+#endif /* CONFIG_VNC_TLS */
+ ret = recv(vs->csock, (void *)data, datalen, 0);
+ VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret);
+ return vnc_client_io_error(vs, ret, socket_error());
+}
+
+
+/*
+ * Called to read data from the client socket to the input buffer,
+ * when not using any SASL SSF encryption layers. Will read as much
+ * data as possible without blocking.
+ *
+ * Returns the number of bytes read. Returns -1 on error, and
+ * disconnects the client socket.
+ */
+static long vnc_client_read_plain(VncState *vs)
+{
+ int 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);
+ ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096);
+ if (!ret)
+ return 0;
+ vs->input.offset += ret;
+ return ret;
+}
+
+
+/*
+ * First function called whenever there is more data to be read from
+ * 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)
+{
+ VncState *vs = opaque;
+ long ret;
+
+#ifdef CONFIG_VNC_SASL
+ if (vs->sasl.conn && vs->sasl.runSSF)
+ ret = vnc_client_read_sasl(vs);
+ else
+#endif /* CONFIG_VNC_SASL */
+ ret = vnc_client_read_plain(vs);
+ if (!ret) {
+ if (vs->csock == -1)
+ vnc_disconnect_finish(vs);
+ return;
+ }
+
+ while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+ size_t len = vs->read_handler_expect;
+ int ret;
+
+ ret = vs->read_handler(vs, vs->input.buffer, len);
+ if (vs->csock == -1) {
+ vnc_disconnect_finish(vs);
+ return;
+ }
+
+ if (!ret) {
+ memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
+ vs->input.offset -= len;
+ } else {
+ vs->read_handler_expect = ret;
+ }
+ }
+}
+
+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_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+ }
+
+ buffer_append(&vs->output, data, len);
+}
+
+void vnc_write_s32(VncState *vs, int32_t value)
+{
+ vnc_write_u32(vs, *(uint32_t *)&value);
+}
+
+void vnc_write_u32(VncState *vs, uint32_t value)
+{
+ uint8_t buf[4];
+
+ buf[0] = (value >> 24) & 0xFF;
+ buf[1] = (value >> 16) & 0xFF;
+ buf[2] = (value >> 8) & 0xFF;
+ buf[3] = value & 0xFF;
+
+ vnc_write(vs, buf, 4);
+}
+
+void vnc_write_u16(VncState *vs, uint16_t value)
+{
+ uint8_t buf[2];
+
+ buf[0] = (value >> 8) & 0xFF;
+ buf[1] = value & 0xFF;
+
+ vnc_write(vs, buf, 2);
+}
+
+void vnc_write_u8(VncState *vs, uint8_t value)
+{
+ vnc_write(vs, (char *)&value, 1);
+}
+
+void vnc_flush(VncState *vs)
+{
+ if (vs->csock != -1 && vs->output.offset)
+ vnc_client_write(vs);
+}
+
+uint8_t read_u8(uint8_t *data, size_t offset)
+{
+ return data[offset];
+}
+
+uint16_t read_u16(uint8_t *data, size_t offset)
+{
+ return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
+}
+
+int32_t read_s32(uint8_t *data, size_t offset)
+{
+ return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+uint32_t read_u32(uint8_t *data, size_t offset)
+{
+ return ((data[offset] << 24) | (data[offset + 1] << 16) |
+ (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
+{
+}
+
+static void check_pointer_type_change(Notifier *notifier)
+{
+ VncState *vs = container_of(notifier, VncState, mouse_mode_notifier);
+ int absolute = kbd_mouse_is_absolute();
+
+ if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) {
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, absolute, 0,
+ ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_POINTER_TYPE_CHANGE);
+ vnc_flush(vs);
+ }
+ vs->absolute = absolute;
+}
+
+static void pointer_event(VncState *vs, int button_mask, int x, int y)
+{
+ int buttons = 0;
+ int dz = 0;
+
+ if (button_mask & 0x01)
+ buttons |= MOUSE_EVENT_LBUTTON;
+ if (button_mask & 0x02)
+ buttons |= MOUSE_EVENT_MBUTTON;
+ if (button_mask & 0x04)
+ buttons |= MOUSE_EVENT_RBUTTON;
+ if (button_mask & 0x08)
+ dz = -1;
+ if (button_mask & 0x10)
+ dz = 1;
+
+ if (vs->absolute) {
+ kbd_mouse_event(ds_get_width(vs->ds) > 1 ?
+ x * 0x7FFF / (ds_get_width(vs->ds) - 1) : 0x4000,
+ ds_get_height(vs->ds) > 1 ?
+ y * 0x7FFF / (ds_get_height(vs->ds) - 1) : 0x4000,
+ dz, buttons);
+ } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) {
+ x -= 0x7FFF;
+ y -= 0x7FFF;
+
+ kbd_mouse_event(x, y, dz, buttons);
+ } else {
+ if (vs->last_x != -1)
+ kbd_mouse_event(x - vs->last_x,
+ y - vs->last_y,
+ dz, buttons);
+ vs->last_x = x;
+ vs->last_y = y;
+ }
+}
+
+static void reset_keys(VncState *vs)
+{
+ int i;
+ for(i = 0; i < 256; i++) {
+ if (vs->modifiers_state[i]) {
+ if (i & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(i | SCANCODE_UP);
+ vs->modifiers_state[i] = 0;
+ }
+ }
+}
+
+static void press_key(VncState *vs, int keysym)
+{
+ int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ kbd_put_keycode(keycode | SCANCODE_UP);
+}
+
+static void kbd_leds(void *opaque, int ledstate)
+{
+ VncState *vs = opaque;
+ int caps, num;
+
+ caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
+ num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0;
+
+ if (vs->modifiers_state[0x3a] != caps) {
+ vs->modifiers_state[0x3a] = caps;
+ }
+ if (vs->modifiers_state[0x45] != num) {
+ vs->modifiers_state[0x45] = num;
+ }
+}
+
+static void do_key_event(VncState *vs, int down, int keycode, int sym)
+{
+ /* QEMU console switch */
+ switch(keycode) {
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ if (down)
+ vs->modifiers_state[keycode] = 1;
+ else
+ vs->modifiers_state[keycode] = 0;
+ break;
+ case 0x02 ... 0x0a: /* '1' to '9' keys */
+ if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
+ /* Reset the modifiers sent to the current console */
+ reset_keys(vs);
+ console_select(keycode - 0x02);
+ return;
+ }
+ break;
+ case 0x3a: /* CapsLock */
+ case 0x45: /* NumLock */
+ if (down)
+ vs->modifiers_state[keycode] ^= 1;
+ break;
+ }
+
+ if (vs->vd->lock_key_sync &&
+ keycode_is_keypad(vs->vd->kbd_layout, keycode)) {
+ /* If the numlock state needs to change then simulate an additional
+ keypress before sending this one. This will happen if the user
+ toggles numlock away from the VNC window.
+ */
+ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
+ if (!vs->modifiers_state[0x45]) {
+ vs->modifiers_state[0x45] = 1;
+ press_key(vs, 0xff7f);
+ }
+ } else {
+ if (vs->modifiers_state[0x45]) {
+ vs->modifiers_state[0x45] = 0;
+ press_key(vs, 0xff7f);
+ }
+ }
+ }
+
+ if (vs->vd->lock_key_sync &&
+ ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) {
+ /* If the capslock state needs to change then simulate an additional
+ keypress before sending this one. This will happen if the user
+ toggles capslock away from the VNC window.
+ */
+ int uppercase = !!(sym >= 'A' && sym <= 'Z');
+ int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]);
+ int capslock = !!(vs->modifiers_state[0x3a]);
+ if (capslock) {
+ if (uppercase == shift) {
+ vs->modifiers_state[0x3a] = 0;
+ press_key(vs, 0xffe5);
+ }
+ } else {
+ if (uppercase != shift) {
+ vs->modifiers_state[0x3a] = 1;
+ press_key(vs, 0xffe5);
+ }
+ }
+ }
+
+ if (is_graphic_console()) {
+ if (keycode & SCANCODE_GREY)
+ kbd_put_keycode(SCANCODE_EMUL0);
+ if (down)
+ kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
+ else
+ kbd_put_keycode(keycode | SCANCODE_UP);
+ } else {
+ /* QEMU console emulation */
+ if (down) {
+ int numlock = vs->modifiers_state[0x45];
+ switch (keycode) {
+ case 0x2a: /* Left Shift */
+ case 0x36: /* Right Shift */
+ case 0x1d: /* Left CTRL */
+ case 0x9d: /* Right CTRL */
+ case 0x38: /* Left ALT */
+ case 0xb8: /* Right ALT */
+ break;
+ case 0xc8:
+ kbd_put_keysym(QEMU_KEY_UP);
+ break;
+ case 0xd0:
+ kbd_put_keysym(QEMU_KEY_DOWN);
+ break;
+ case 0xcb:
+ kbd_put_keysym(QEMU_KEY_LEFT);
+ break;
+ case 0xcd:
+ kbd_put_keysym(QEMU_KEY_RIGHT);
+ break;
+ case 0xd3:
+ kbd_put_keysym(QEMU_KEY_DELETE);
+ break;
+ case 0xc7:
+ kbd_put_keysym(QEMU_KEY_HOME);
+ break;
+ case 0xcf:
+ kbd_put_keysym(QEMU_KEY_END);
+ break;
+ case 0xc9:
+ kbd_put_keysym(QEMU_KEY_PAGEUP);
+ break;
+ case 0xd1:
+ kbd_put_keysym(QEMU_KEY_PAGEDOWN);
+ break;
+
+ case 0x47:
+ kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME);
+ break;
+ case 0x48:
+ kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP);
+ break;
+ case 0x49:
+ kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP);
+ break;
+ case 0x4b:
+ kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT);
+ break;
+ case 0x4c:
+ kbd_put_keysym('5');
+ break;
+ case 0x4d:
+ kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT);
+ break;
+ case 0x4f:
+ kbd_put_keysym(numlock ? '1' : QEMU_KEY_END);
+ break;
+ case 0x50:
+ kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN);
+ break;
+ case 0x51:
+ kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN);
+ break;
+ case 0x52:
+ kbd_put_keysym('0');
+ break;
+ case 0x53:
+ kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE);
+ break;
+
+ case 0xb5:
+ kbd_put_keysym('/');
+ break;
+ case 0x37:
+ kbd_put_keysym('*');
+ break;
+ case 0x4a:
+ kbd_put_keysym('-');
+ break;
+ case 0x4e:
+ kbd_put_keysym('+');
+ break;
+ case 0x9c:
+ kbd_put_keysym('\n');
+ break;
+
+ default:
+ kbd_put_keysym(sym);
+ break;
+ }
+ }
+ }
+}
+
+static void key_event(VncState *vs, int down, uint32_t sym)
+{
+ int keycode;
+ int lsym = sym;
+
+ if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) {
+ lsym = lsym - 'A' + 'a';
+ }
+
+ keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
+ do_key_event(vs, down, keycode, sym);
+}
+
+static void ext_key_event(VncState *vs, int down,
+ uint32_t sym, uint16_t keycode)
+{
+ /* if the user specifies a keyboard layout, always use it */
+ if (keyboard_layout)
+ key_event(vs, down, sym);
+ else
+ do_key_event(vs, down, keycode, sym);
+}
+
+static void framebuffer_update_request(VncState *vs, int incremental,
+ int x_position, int y_position,
+ int w, int h)
+{
+ if (y_position > ds_get_height(vs->ds))
+ y_position = ds_get_height(vs->ds);
+ if (y_position + h >= ds_get_height(vs->ds))
+ h = ds_get_height(vs->ds) - y_position;
+
+ int i;
+ vs->need_update = 1;
+ if (!incremental) {
+ vs->force_update = 1;
+ for (i = 0; i < h; i++) {
+ vnc_set_bits(vs->dirty[y_position + i],
+ (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS);
+ }
+ }
+}
+
+static void send_ext_key_event_ack(VncState *vs)
+{
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_EXT_KEY_EVENT);
+ vnc_flush(vs);
+}
+
+static void send_ext_audio_ack(VncState *vs)
+{
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds),
+ VNC_ENCODING_AUDIO);
+ vnc_flush(vs);
+}
+
+static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
+{
+ int i;
+ unsigned int enc = 0;
+
+ vs->features = 0;
+ vs->vnc_encoding = 0;
+ vs->tight_compression = 9;
+ vs->tight_quality = -1; /* Lossless by default */
+ vs->absolute = -1;
+
+ /*
+ * Start from the end because the encodings are sent in order of preference.
+ * This way the prefered encoding (first encoding defined in the array)
+ * will be set at the end of the loop.
+ */
+ for (i = n_encodings - 1; i >= 0; i--) {
+ enc = encodings[i];
+ switch (enc) {
+ case VNC_ENCODING_RAW:
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_COPYRECT:
+ vs->features |= VNC_FEATURE_COPYRECT_MASK;
+ break;
+ case VNC_ENCODING_HEXTILE:
+ vs->features |= VNC_FEATURE_HEXTILE_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_TIGHT:
+ vs->features |= VNC_FEATURE_TIGHT_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_ZLIB:
+ vs->features |= VNC_FEATURE_ZLIB_MASK;
+ vs->vnc_encoding = enc;
+ break;
+ case VNC_ENCODING_DESKTOPRESIZE:
+ vs->features |= VNC_FEATURE_RESIZE_MASK;
+ break;
+ case VNC_ENCODING_POINTER_TYPE_CHANGE:
+ vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK;
+ break;
+ case VNC_ENCODING_RICH_CURSOR:
+ vs->features |= VNC_FEATURE_RICH_CURSOR_MASK;
+ break;
+ case VNC_ENCODING_EXT_KEY_EVENT:
+ send_ext_key_event_ack(vs);
+ break;
+ case VNC_ENCODING_AUDIO:
+ send_ext_audio_ack(vs);
+ break;
+ case VNC_ENCODING_WMVi:
+ vs->features |= VNC_FEATURE_WMVI_MASK;
+ break;
+ case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
+ vs->tight_compression = (enc & 0x0F);
+ break;
+ case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9:
+ vs->tight_quality = (enc & 0x0F);
+ break;
+ default:
+ VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc);
+ break;
+ }
+ }
+ vnc_desktop_resize(vs);
+ check_pointer_type_change(&vs->mouse_mode_notifier);
+}
+
+static void set_pixel_conversion(VncState *vs)
+{
+ if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) ==
+ (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) &&
+ !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) {
+ vs->write_pixels = vnc_write_pixels_copy;
+ vnc_hextile_set_pixel_conversion(vs, 0);
+ } else {
+ vs->write_pixels = vnc_write_pixels_generic;
+ vnc_hextile_set_pixel_conversion(vs, 1);
+ }
+}
+
+static void set_pixel_format(VncState *vs,
+ int bits_per_pixel, int depth,
+ int big_endian_flag, int true_color_flag,
+ int red_max, int green_max, int blue_max,
+ int red_shift, int green_shift, int blue_shift)
+{
+ if (!true_color_flag) {
+ vnc_client_error(vs);
+ return;
+ }
+
+ vs->clientds = *(vs->vd->guest.ds);
+ vs->clientds.pf.rmax = red_max;
+ count_bits(vs->clientds.pf.rbits, red_max);
+ vs->clientds.pf.rshift = red_shift;
+ vs->clientds.pf.rmask = red_max << red_shift;
+ vs->clientds.pf.gmax = green_max;
+ count_bits(vs->clientds.pf.gbits, green_max);
+ vs->clientds.pf.gshift = green_shift;
+ vs->clientds.pf.gmask = green_max << green_shift;
+ vs->clientds.pf.bmax = blue_max;
+ count_bits(vs->clientds.pf.bbits, blue_max);
+ vs->clientds.pf.bshift = blue_shift;
+ vs->clientds.pf.bmask = blue_max << blue_shift;
+ vs->clientds.pf.bits_per_pixel = bits_per_pixel;
+ vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8;
+ vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel;
+ vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00;
+
+ set_pixel_conversion(vs);
+
+ vga_hw_invalidate();
+ vga_hw_update();
+}
+
+static void pixel_format_message (VncState *vs) {
+ char pad[3] = { 0, 0, 0 };
+
+ vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */
+ vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */
+
+#ifdef HOST_WORDS_BIGENDIAN
+ vnc_write_u8(vs, 1); /* big-endian-flag */
+#else
+ vnc_write_u8(vs, 0); /* big-endian-flag */
+#endif
+ vnc_write_u8(vs, 1); /* true-color-flag */
+ vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */
+ vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */
+ vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */
+ vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */
+ vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */
+ vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */
+
+ vnc_hextile_set_pixel_conversion(vs, 0);
+
+ vs->clientds = *(vs->ds->surface);
+ vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG;
+ vs->write_pixels = vnc_write_pixels_copy;
+
+ vnc_write(vs, pad, 3); /* padding */
+}
+
+static void vnc_dpy_setdata(DisplayState *ds)
+{
+ /* We don't have to do anything */
+}
+
+static void vnc_colordepth(VncState *vs)
+{
+ if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) {
+ /* Sending a WMVi message to notify the client*/
+ vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1); /* number of rects */
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds),
+ ds_get_height(vs->ds), VNC_ENCODING_WMVi);
+ pixel_format_message(vs);
+ vnc_flush(vs);
+ } else {
+ set_pixel_conversion(vs);
+ }
+}
+
+static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
+{
+ int i;
+ uint16_t limit;
+ VncDisplay *vd = vs->vd;
+
+ if (data[0] > 3) {
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval))
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+ }
+
+ switch (data[0]) {
+ case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
+ if (len == 1)
+ return 20;
+
+ set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+ read_u8(data, 6), read_u8(data, 7),
+ read_u16(data, 8), read_u16(data, 10),
+ read_u16(data, 12), read_u8(data, 14),
+ read_u8(data, 15), read_u8(data, 16));
+ break;
+ case VNC_MSG_CLIENT_SET_ENCODINGS:
+ if (len == 1)
+ return 4;
+
+ if (len == 4) {
+ limit = read_u16(data, 2);
+ if (limit > 0)
+ return 4 + (limit * 4);
+ } else
+ limit = read_u16(data, 2);
+
+ for (i = 0; i < limit; i++) {
+ int32_t val = read_s32(data, 4 + (i * 4));
+ memcpy(data + 4 + (i * 4), &val, sizeof(val));
+ }
+
+ set_encodings(vs, (int32_t *)(data + 4), limit);
+ break;
+ case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
+ if (len == 1)
+ return 10;
+
+ framebuffer_update_request(vs,
+ read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
+ read_u16(data, 6), read_u16(data, 8));
+ break;
+ case VNC_MSG_CLIENT_KEY_EVENT:
+ if (len == 1)
+ return 8;
+
+ key_event(vs, read_u8(data, 1), read_u32(data, 4));
+ break;
+ case VNC_MSG_CLIENT_POINTER_EVENT:
+ if (len == 1)
+ return 6;
+
+ pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
+ break;
+ case VNC_MSG_CLIENT_CUT_TEXT:
+ if (len == 1)
+ return 8;
+
+ if (len == 8) {
+ uint32_t dlen = read_u32(data, 4);
+ if (dlen > 0)
+ return 8 + dlen;
+ }
+
+ client_cut_text(vs, read_u32(data, 4), data + 8);
+ break;
+ case VNC_MSG_CLIENT_QEMU:
+ if (len == 1)
+ return 2;
+
+ switch (read_u8(data, 1)) {
+ case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
+ if (len == 2)
+ return 12;
+
+ ext_key_event(vs, read_u16(data, 2),
+ read_u32(data, 4), read_u32(data, 8));
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO:
+ if (len == 2)
+ return 4;
+
+ switch (read_u16 (data, 2)) {
+ case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
+ audio_add(vs);
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
+ audio_del(vs);
+ break;
+ case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
+ if (len == 4)
+ return 10;
+ switch (read_u8(data, 4)) {
+ case 0: vs->as.fmt = AUD_FMT_U8; break;
+ case 1: vs->as.fmt = AUD_FMT_S8; break;
+ case 2: vs->as.fmt = AUD_FMT_U16; break;
+ case 3: vs->as.fmt = AUD_FMT_S16; break;
+ case 4: vs->as.fmt = AUD_FMT_U32; break;
+ case 5: vs->as.fmt = AUD_FMT_S32; break;
+ default:
+ printf("Invalid audio format %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.nchannels = read_u8(data, 5);
+ if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
+ printf("Invalid audio channel coount %d\n",
+ read_u8(data, 5));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.freq = read_u32(data, 6);
+ break;
+ default:
+ printf ("Invalid audio message %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ break;
+
+ default:
+ printf("Msg: %d\n", read_u16(data, 0));
+ vnc_client_error(vs);
+ break;
+ }
+ break;
+ default:
+ printf("Msg: %d\n", data[0]);
+ vnc_client_error(vs);
+ break;
+ }
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+ return 0;
+}
+
+static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
+{
+ char buf[1024];
+ int size;
+
+ vs->client_width = ds_get_width(vs->ds);
+ vs->client_height = ds_get_height(vs->ds);
+ vnc_write_u16(vs, vs->client_width);
+ vnc_write_u16(vs, vs->client_height);
+
+ pixel_format_message(vs);
+
+ if (qemu_name)
+ size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
+ else
+ size = snprintf(buf, sizeof(buf), "QEMU");
+
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
+ vnc_flush(vs);
+
+ vnc_client_cache_auth(vs);
+ vnc_qmp_event(vs, QEVENT_VNC_INITIALIZED);
+
+ vnc_read_when(vs, protocol_client_msg, 1);
+
+ return 0;
+}
+
+void start_client_init(VncState *vs)
+{
+ vnc_read_when(vs, protocol_client_init, 1);
+}
+
+static void make_challenge(VncState *vs)
+{
+ int i;
+
+ srand(time(NULL)+getpid()+getpid()*987654+rand());
+
+ for (i = 0 ; i < sizeof(vs->challenge) ; i++)
+ vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+}
+
+static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
+{
+ unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
+ int i, j, pwlen;
+ unsigned char key[8];
+
+ if (!vs->vd->password || !vs->vd->password[0]) {
+ VNC_DEBUG("No password configured on server");
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
+ }
+
+ memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
+
+ /* Calculate the expected challenge response */
+ pwlen = strlen(vs->vd->password);
+ for (i=0; i<sizeof(key); i++)
+ key[i] = i<pwlen ? vs->vd->password[i] : 0;
+ deskey(key, EN0);
+ for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
+ des(response+j, response+j);
+
+ /* Compare expected vs actual challenge response */
+ if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
+ VNC_DEBUG("Client challenge reponse did not match\n");
+ vnc_write_u32(vs, 1); /* Reject auth */
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ } else {
+ VNC_DEBUG("Accepting VNC challenge response\n");
+ vnc_write_u32(vs, 0); /* Accept auth */
+ vnc_flush(vs);
+
+ start_client_init(vs);
+ }
+ return 0;
+}
+
+void start_auth_vnc(VncState *vs)
+{
+ make_challenge(vs);
+ /* Send client a 'random' challenge */
+ vnc_write(vs, vs->challenge, sizeof(vs->challenge));
+ vnc_flush(vs);
+
+ vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
+}
+
+
+static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
+{
+ /* We only advertise 1 auth scheme at a time, so client
+ * must pick the one we sent. Verify this */
+ if (data[0] != vs->vd->auth) { /* Reject auth */
+ VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]);
+ vnc_write_u32(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ } else { /* Accept requested auth */
+ VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
+ switch (vs->vd->auth) {
+ case VNC_AUTH_NONE:
+ VNC_DEBUG("Accept auth none\n");
+ if (vs->minor >= 8) {
+ vnc_write_u32(vs, 0); /* Accept auth completion */
+ vnc_flush(vs);
+ }
+ start_client_init(vs);
+ break;
+
+ case VNC_AUTH_VNC:
+ VNC_DEBUG("Start VNC auth\n");
+ 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:
+ VNC_DEBUG("Accept SASL auth\n");
+ start_auth_sasl(vs);
+ break;
+#endif /* CONFIG_VNC_SASL */
+
+ default: /* Should not be possible, but just in case */
+ VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth);
+ vnc_write_u8(vs, 1);
+ if (vs->minor >= 8) {
+ static const char err[] = "Authentication failed";
+ vnc_write_u32(vs, sizeof(err));
+ vnc_write(vs, err, sizeof(err));
+ }
+ vnc_client_error(vs);
+ }
+ }
+ return 0;
+}
+
+static int protocol_version(VncState *vs, uint8_t *version, size_t len)
+{
+ char local[13];
+
+ memcpy(local, version, 12);
+ local[12] = 0;
+
+ if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
+ VNC_DEBUG("Malformed protocol version %s\n", local);
+ vnc_client_error(vs);
+ return 0;
+ }
+ VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
+ if (vs->major != 3 ||
+ (vs->minor != 3 &&
+ vs->minor != 4 &&
+ vs->minor != 5 &&
+ vs->minor != 7 &&
+ vs->minor != 8)) {
+ VNC_DEBUG("Unsupported client version\n");
+ vnc_write_u32(vs, VNC_AUTH_INVALID);
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ return 0;
+ }
+ /* Some broken clients report v3.4 or v3.5, which spec requires to be treated
+ * as equivalent to v3.3 by servers
+ */
+ if (vs->minor == 4 || vs->minor == 5)
+ vs->minor = 3;
+
+ if (vs->minor == 3) {
+ if (vs->vd->auth == VNC_AUTH_NONE) {
+ VNC_DEBUG("Tell client auth none\n");
+ vnc_write_u32(vs, vs->vd->auth);
+ vnc_flush(vs);
+ start_client_init(vs);
+ } else if (vs->vd->auth == VNC_AUTH_VNC) {
+ VNC_DEBUG("Tell client VNC auth\n");
+ vnc_write_u32(vs, vs->vd->auth);
+ vnc_flush(vs);
+ start_auth_vnc(vs);
+ } else {
+ VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth);
+ vnc_write_u32(vs, VNC_AUTH_INVALID);
+ vnc_flush(vs);
+ vnc_client_error(vs);
+ }
+ } else {
+ VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth);
+ vnc_write_u8(vs, 1); /* num auth */
+ vnc_write_u8(vs, vs->vd->auth);
+ vnc_read_when(vs, protocol_client_auth, 1);
+ vnc_flush(vs);
+ }
+
+ return 0;
+}
+
+static int vnc_refresh_server_surface(VncDisplay *vd)
+{
+ int y;
+ uint8_t *guest_row;
+ uint8_t *server_row;
+ int cmp_bytes;
+ uint32_t width_mask[VNC_DIRTY_WORDS];
+ VncState *vs;
+ int has_dirty = 0;
+
+ /*
+ * Walk through the guest dirty map.
+ * Check and copy modified bits from guest to server surface.
+ * Update server dirty map.
+ */
+ vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS);
+ cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds);
+ guest_row = vd->guest.ds->data;
+ server_row = vd->server->data;
+ for (y = 0; y < vd->guest.ds->height; y++) {
+ if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) {
+ int x;
+ uint8_t *guest_ptr;
+ uint8_t *server_ptr;
+
+ guest_ptr = guest_row;
+ server_ptr = server_row;
+
+ for (x = 0; x < vd->guest.ds->width;
+ x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) {
+ if (!vnc_get_bit(vd->guest.dirty[y], (x / 16)))
+ continue;
+ vnc_clear_bit(vd->guest.dirty[y], (x / 16));
+ if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0)
+ continue;
+ memcpy(server_ptr, guest_ptr, cmp_bytes);
+ QTAILQ_FOREACH(vs, &vd->clients, next) {
+ vnc_set_bit(vs->dirty[y], (x / 16));
+ }
+ has_dirty++;
+ }
+ }
+ guest_row += ds_get_linesize(vd->ds);
+ server_row += ds_get_linesize(vd->ds);
+ }
+ return has_dirty;
+}
+
+static void vnc_refresh(void *opaque)
+{
+ VncDisplay *vd = opaque;
+ VncState *vs, *vn;
+ int has_dirty, rects = 0;
+
+ vga_hw_update();
+
+ has_dirty = vnc_refresh_server_surface(vd);
+
+ QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) {
+ rects += vnc_update_client(vs, has_dirty);
+ /* vs might be free()ed here */
+ }
+ /* vd->timer could be NULL now if the last client disconnected,
+ * in this case don't update the timer */
+ if (vd->timer == NULL)
+ return;
+
+ if (has_dirty && rects) {
+ vd->timer_interval /= 2;
+ if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE)
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ } else {
+ vd->timer_interval += VNC_REFRESH_INTERVAL_INC;
+ if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX)
+ vd->timer_interval = VNC_REFRESH_INTERVAL_MAX;
+ }
+ qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval);
+}
+
+static void vnc_init_timer(VncDisplay *vd)
+{
+ vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+ if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) {
+ vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd);
+ vnc_refresh(vd);
+ }
+}
+
+static void vnc_remove_timer(VncDisplay *vd)
+{
+ if (vd->timer != NULL && QTAILQ_EMPTY(&vd->clients)) {
+ qemu_del_timer(vd->timer);
+ qemu_free_timer(vd->timer);
+ vd->timer = NULL;
+ }
+}
+
+static void vnc_connect(VncDisplay *vd, int csock)
+{
+ VncState *vs = qemu_mallocz(sizeof(VncState));
+ vs->csock = csock;
+
+ VNC_DEBUG("New client on socket %d\n", csock);
+ dcl->idle = 0;
+ socket_set_nonblock(vs->csock);
+ qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+
+ vnc_client_cache_addr(vs);
+ vnc_qmp_event(vs, QEVENT_VNC_CONNECTED);
+
+ vs->vd = vd;
+ vs->ds = vd->ds;
+ vs->last_x = -1;
+ vs->last_y = -1;
+
+ vs->as.freq = 44100;
+ vs->as.nchannels = 2;
+ vs->as.fmt = AUD_FMT_S16;
+ vs->as.endianness = 0;
+
+ QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
+
+ vga_hw_update();
+
+ vnc_write(vs, "RFB 003.008\n", 12);
+ vnc_flush(vs);
+ vnc_read_when(vs, protocol_version, 12);
+ reset_keys(vs);
+ if (vs->vd->lock_key_sync)
+ vs->led = qemu_add_led_event_handler(kbd_leds, vs);
+
+ vs->mouse_mode_notifier.notify = check_pointer_type_change;
+ qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
+
+ vnc_init_timer(vd);
+
+ /* vs might be free()ed here */
+}
+
+static void vnc_listen_read(void *opaque)
+{
+ VncDisplay *vs = opaque;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+
+ /* Catch-up */
+ vga_hw_update();
+
+ int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
+ if (csock != -1) {
+ vnc_connect(vs, csock);
+ }
+}
+
+void vnc_display_init(DisplayState *ds)
+{
+ VncDisplay *vs = qemu_mallocz(sizeof(*vs));
+
+ dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+
+ ds->opaque = vs;
+ dcl->idle = 1;
+ vnc_display = vs;
+
+ vs->lsock = -1;
+
+ vs->ds = ds;
+ QTAILQ_INIT(&vs->clients);
+
+ if (keyboard_layout)
+ vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
+ else
+ vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
+
+ if (!vs->kbd_layout)
+ exit(1);
+
+ dcl->dpy_copy = vnc_dpy_copy;
+ dcl->dpy_update = vnc_dpy_update;
+ dcl->dpy_resize = vnc_dpy_resize;
+ dcl->dpy_setdata = vnc_dpy_setdata;
+ register_displaychangelistener(ds, dcl);
+ ds->mouse_set = vnc_mouse_set;
+ ds->cursor_define = vnc_dpy_cursor_define;
+}
+
+
+void vnc_display_close(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs)
+ return;
+ if (vs->display) {
+ qemu_free(vs->display);
+ vs->display = NULL;
+ }
+ if (vs->lsock != -1) {
+ qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+ close(vs->lsock);
+ vs->lsock = -1;
+ }
+ vs->auth = VNC_AUTH_INVALID;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ vs->tls.x509verify = 0;
+#endif
+}
+
+int vnc_display_password(DisplayState *ds, const char *password)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ if (!vs) {
+ return -1;
+ }
+
+ if (vs->password) {
+ qemu_free(vs->password);
+ vs->password = NULL;
+ }
+ if (password && password[0]) {
+ if (!(vs->password = qemu_strdup(password)))
+ return -1;
+ if (vs->auth == VNC_AUTH_NONE) {
+ vs->auth = VNC_AUTH_VNC;
+ }
+ } else {
+ vs->auth = VNC_AUTH_NONE;
+ }
+
+ return 0;
+}
+
+char *vnc_display_local_addr(DisplayState *ds)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+
+ return vnc_socket_local_addr("%s:%s", vs->lsock);
+}
+
+int vnc_display_open(DisplayState *ds, const char *display)
+{
+ VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
+ const char *options;
+ int password = 0;
+ int reverse = 0;
+#ifdef CONFIG_VNC_TLS
+ int tls = 0, x509 = 0;
+#endif
+#ifdef CONFIG_VNC_SASL
+ int sasl = 0;
+ int saslErr;
+#endif
+ int acl = 0;
+ int lock_key_sync = 1;
+
+ if (!vnc_display)
+ return -1;
+ vnc_display_close(ds);
+ if (strcmp(display, "none") == 0)
+ return 0;
+
+ if (!(vs->display = strdup(display)))
+ return -1;
+
+ options = display;
+ while ((options = strchr(options, ','))) {
+ options++;
+ if (strncmp(options, "password", 8) == 0) {
+ password = 1; /* Require password auth */
+ } else if (strncmp(options, "reverse", 7) == 0) {
+ reverse = 1;
+ } else if (strncmp(options, "no-lock-key-sync", 9) == 0) {
+ lock_key_sync = 0;
+#ifdef CONFIG_VNC_SASL
+ } else if (strncmp(options, "sasl", 4) == 0) {
+ sasl = 1; /* Require SASL auth */
+#endif
+#ifdef CONFIG_VNC_TLS
+ } else if (strncmp(options, "tls", 3) == 0) {
+ tls = 1; /* Require TLS */
+ } else if (strncmp(options, "x509", 4) == 0) {
+ char *start, *end;
+ x509 = 1; /* Require x509 certificates */
+ if (strncmp(options, "x509verify", 10) == 0)
+ vs->tls.x509verify = 1; /* ...and verify client certs */
+
+ /* Now check for 'x509=/some/path' postfix
+ * and use that to setup x509 certificate/key paths */
+ start = strchr(options, '=');
+ end = strchr(options, ',');
+ if (start && (!end || (start < end))) {
+ int len = end ? end-(start+1) : strlen(start+1);
+ char *path = qemu_strndup(start + 1, len);
+
+ VNC_DEBUG("Trying certificate path '%s'\n", path);
+ if (vnc_tls_set_x509_creds_dir(vs, path) < 0) {
+ fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
+ qemu_free(path);
+ qemu_free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+ qemu_free(path);
+ } else {
+ fprintf(stderr, "No certificate path provided\n");
+ qemu_free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+#endif
+ } else if (strncmp(options, "acl", 3) == 0) {
+ acl = 1;
+ } else if (strncmp(options, "lossy", 5) == 0) {
+ vs->lossy = true;
+ }
+ }
+
+#ifdef CONFIG_VNC_TLS
+ if (acl && x509 && vs->tls.x509verify) {
+ if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) {
+ fprintf(stderr, "Failed to create x509 dname ACL\n");
+ exit(1);
+ }
+ }
+#endif
+#ifdef CONFIG_VNC_SASL
+ if (acl && sasl) {
+ if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) {
+ fprintf(stderr, "Failed to create username ACL\n");
+ exit(1);
+ }
+ }
+#endif
+
+ /*
+ * Combinations we support here:
+ *
+ * - no-auth (clear text, no auth)
+ * - password (clear text, weak auth)
+ * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI)
+ * - tls (encrypt, weak anonymous creds, no auth)
+ * - tls + password (encrypt, weak anonymous creds, weak auth)
+ * - tls + sasl (encrypt, weak anonymous creds, good auth)
+ * - tls + x509 (encrypt, good x509 creds, no auth)
+ * - tls + x509 + password (encrypt, good x509 creds, weak auth)
+ * - tls + x509 + sasl (encrypt, good x509 creds, good auth)
+ *
+ * NB1. TLS is a stackable auth scheme.
+ * NB2. the x509 schemes have option to validate a client cert dname
+ */
+ if (password) {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+ }
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Initializing VNC server with password auth\n");
+ vs->auth = VNC_AUTH_VNC;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif /* CONFIG_VNC_TLS */
+#ifdef CONFIG_VNC_SASL
+ } else if (sasl) {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509SASL;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS SASL auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL;
+ }
+ } else {
+#endif /* CONFIG_VNC_TLS */
+ VNC_DEBUG("Initializing VNC server with SASL auth\n");
+ vs->auth = VNC_AUTH_SASL;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif /* CONFIG_VNC_TLS */
+#endif /* CONFIG_VNC_SASL */
+ } else {
+#ifdef CONFIG_VNC_TLS
+ if (tls) {
+ vs->auth = VNC_AUTH_VENCRYPT;
+ if (x509) {
+ VNC_DEBUG("Initializing VNC server with x509 no auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
+ } else {
+ VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+ vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+ }
+ } else {
+#endif
+ VNC_DEBUG("Initializing VNC server with no auth\n");
+ vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+ vs->subauth = VNC_AUTH_INVALID;
+ }
+#endif
+ }
+
+#ifdef CONFIG_VNC_SASL
+ if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) {
+ fprintf(stderr, "Failed to initialize SASL auth %s",
+ sasl_errstring(saslErr, NULL, NULL));
+ free(vs->display);
+ vs->display = NULL;
+ return -1;
+ }
+#endif
+ vs->lock_key_sync = lock_key_sync;
+
+ if (reverse) {
+ /* connect to viewer */
+ if (strncmp(display, "unix:", 5) == 0)
+ vs->lsock = unix_connect(display+5);
+ else
+ vs->lsock = inet_connect(display, SOCK_STREAM);
+ if (-1 == vs->lsock) {
+ free(vs->display);
+ vs->display = NULL;
+ return -1;
+ } else {
+ int csock = vs->lsock;
+ vs->lsock = -1;
+ vnc_connect(vs, csock);
+ }
+ return 0;
+
+ } else {
+ /* listen for connects */
+ char *dpy;
+ dpy = qemu_malloc(256);
+ if (strncmp(display, "unix:", 5) == 0) {
+ pstrcpy(dpy, 256, "unix:");
+ vs->lsock = unix_listen(display+5, dpy+5, 256-5);
+ } else {
+ vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900);
+ }
+ if (-1 == vs->lsock) {
+ free(dpy);
+ return -1;
+ } else {
+ free(vs->display);
+ vs->display = dpy;
+ }
+ }
+ return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs);
+}
diff --git a/ui/vnc.h b/ui/vnc.h
new file mode 100644
index 0000000000..ec90cd389c
--- /dev/null
+++ b/ui/vnc.h
@@ -0,0 +1,430 @@
+/*
+ * QEMU VNC display driver
+ *
+ * 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_H
+#define __QEMU_VNC_H
+
+#include "qemu-common.h"
+#include "qemu-queue.h"
+#include "console.h"
+#include "monitor.h"
+#include "audio/audio.h"
+#include <zlib.h>
+#include <stdbool.h>
+
+#include "keymaps.h"
+
+// #define _VNC_DEBUG 1
+
+#ifdef _VNC_DEBUG
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define VNC_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+/*****************************************************************************
+ *
+ * Core data structures
+ *
+ *****************************************************************************/
+
+typedef struct Buffer
+{
+ size_t capacity;
+ size_t offset;
+ uint8_t *buffer;
+} Buffer;
+
+typedef struct VncState VncState;
+
+typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
+
+typedef void VncWritePixels(VncState *vs, struct PixelFormat *pf, void *data, int size);
+
+typedef void VncSendHextileTile(VncState *vs,
+ int x, int y, int w, int h,
+ void *last_bg,
+ void *last_fg,
+ int *has_bg, int *has_fg);
+
+#define VNC_MAX_WIDTH 2560
+#define VNC_MAX_HEIGHT 2048
+#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
+
+#define VNC_AUTH_CHALLENGE_SIZE 16
+
+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
+
+struct VncSurface
+{
+ uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+ DisplaySurface *ds;
+};
+
+struct VncDisplay
+{
+ QTAILQ_HEAD(, VncState) clients;
+ QEMUTimer *timer;
+ int timer_interval;
+ int lsock;
+ DisplayState *ds;
+ kbd_layout_t *kbd_layout;
+ int lock_key_sync;
+
+ QEMUCursor *cursor;
+ int cursor_msize;
+ uint8_t *cursor_mask;
+
+ struct VncSurface guest; /* guest visible surface (aka ds->surface) */
+ DisplaySurface *server; /* vnc server surface */
+
+ char *display;
+ char *password;
+ int auth;
+ bool lossy;
+#ifdef CONFIG_VNC_TLS
+ int subauth; /* Used by VeNCrypt */
+ VncDisplayTLS tls;
+#endif
+#ifdef CONFIG_VNC_SASL
+ VncDisplaySASL sasl;
+#endif
+};
+
+struct VncState
+{
+ int csock;
+
+ DisplayState *ds;
+ uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+
+ VncDisplay *vd;
+ int need_update;
+ int force_update;
+ uint32_t features;
+ int absolute;
+ int last_x;
+ int last_y;
+ int client_width;
+ int client_height;
+
+ uint32_t vnc_encoding;
+
+ int major;
+ int minor;
+
+ char challenge[VNC_AUTH_CHALLENGE_SIZE];
+#ifdef CONFIG_VNC_TLS
+ VncStateTLS tls;
+#endif
+#ifdef CONFIG_VNC_SASL
+ VncStateSASL sasl;
+#endif
+
+ QObject *info;
+
+ Buffer output;
+ Buffer input;
+ /* current output mode information */
+ VncWritePixels *write_pixels;
+ DisplaySurface clientds;
+
+ CaptureVoiceOut *audio_cap;
+ struct audsettings as;
+
+ VncReadEvent *read_handler;
+ size_t read_handler_expect;
+ /* input */
+ uint8_t modifiers_state[256];
+ QEMUPutLEDEntry *led;
+
+ /* Encoding specific */
+
+ /* Tight */
+ uint8_t tight_quality;
+ uint8_t tight_compression;
+ uint8_t tight_pixel24;
+ Buffer tight;
+ Buffer tight_tmp;
+ Buffer tight_zlib;
+ Buffer tight_gradient;
+#ifdef CONFIG_VNC_JPEG
+ Buffer tight_jpeg;
+#endif
+ int tight_levels[4];
+ z_stream tight_stream[4];
+
+ /* Hextile */
+ VncSendHextileTile *send_hextile_tile;
+
+ /* Zlib */
+ Buffer zlib;
+ Buffer zlib_tmp;
+ z_stream zlib_stream;
+ int zlib_level;
+
+ Notifier mouse_mode_notifier;
+
+ QTAILQ_ENTRY(VncState) next;
+};
+
+
+/*****************************************************************************
+ *
+ * Authentication modes
+ *
+ *****************************************************************************/
+
+enum {
+ VNC_AUTH_INVALID = 0,
+ VNC_AUTH_NONE = 1,
+ VNC_AUTH_VNC = 2,
+ VNC_AUTH_RA2 = 5,
+ VNC_AUTH_RA2NE = 6,
+ VNC_AUTH_TIGHT = 16,
+ VNC_AUTH_ULTRA = 17,
+ VNC_AUTH_TLS = 18, /* Supported in GTK-VNC & VINO */
+ VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */
+ VNC_AUTH_SASL = 20, /* Supported in GTK-VNC & VINO */
+};
+
+enum {
+ VNC_AUTH_VENCRYPT_PLAIN = 256,
+ VNC_AUTH_VENCRYPT_TLSNONE = 257,
+ VNC_AUTH_VENCRYPT_TLSVNC = 258,
+ VNC_AUTH_VENCRYPT_TLSPLAIN = 259,
+ VNC_AUTH_VENCRYPT_X509NONE = 260,
+ VNC_AUTH_VENCRYPT_X509VNC = 261,
+ VNC_AUTH_VENCRYPT_X509PLAIN = 262,
+ VNC_AUTH_VENCRYPT_X509SASL = 263,
+ VNC_AUTH_VENCRYPT_TLSSASL = 264,
+};
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define VNC_ENCODING_RAW 0x00000000
+#define VNC_ENCODING_COPYRECT 0x00000001
+#define VNC_ENCODING_RRE 0x00000002
+#define VNC_ENCODING_CORRE 0x00000004
+#define VNC_ENCODING_HEXTILE 0x00000005
+#define VNC_ENCODING_ZLIB 0x00000006
+#define VNC_ENCODING_TIGHT 0x00000007
+#define VNC_ENCODING_ZLIBHEX 0x00000008
+#define VNC_ENCODING_TRLE 0x0000000f
+#define VNC_ENCODING_ZRLE 0x00000010
+#define VNC_ENCODING_ZYWRLE 0x00000011
+#define VNC_ENCODING_COMPRESSLEVEL0 0xFFFFFF00 /* -256 */
+#define VNC_ENCODING_QUALITYLEVEL0 0xFFFFFFE0 /* -32 */
+#define VNC_ENCODING_XCURSOR 0xFFFFFF10 /* -240 */
+#define VNC_ENCODING_RICH_CURSOR 0xFFFFFF11 /* -239 */
+#define VNC_ENCODING_POINTER_POS 0xFFFFFF18 /* -232 */
+#define VNC_ENCODING_LASTRECT 0xFFFFFF20 /* -224 */
+#define VNC_ENCODING_DESKTOPRESIZE 0xFFFFFF21 /* -223 */
+#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */
+#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */
+#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */
+#define VNC_ENCODING_WMVi 0x574D5669
+
+/*****************************************************************************
+ *
+ * Other tight constants
+ *
+ *****************************************************************************/
+
+/*
+ * Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC.
+ */
+
+#define VNC_TIGHT_CCB_RESET_MASK (0x0f)
+#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4)
+#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4)
+#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4)
+#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4)
+#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4)
+#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4)
+
+/*****************************************************************************
+ *
+ * Features
+ *
+ *****************************************************************************/
+
+#define VNC_FEATURE_RESIZE 0
+#define VNC_FEATURE_HEXTILE 1
+#define VNC_FEATURE_POINTER_TYPE_CHANGE 2
+#define VNC_FEATURE_WMVI 3
+#define VNC_FEATURE_TIGHT 4
+#define VNC_FEATURE_ZLIB 5
+#define VNC_FEATURE_COPYRECT 6
+#define VNC_FEATURE_RICH_CURSOR 7
+
+#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE)
+#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE)
+#define VNC_FEATURE_POINTER_TYPE_CHANGE_MASK (1 << VNC_FEATURE_POINTER_TYPE_CHANGE)
+#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI)
+#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT)
+#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB)
+#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT)
+#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR)
+
+
+/* Client -> Server message IDs */
+#define VNC_MSG_CLIENT_SET_PIXEL_FORMAT 0
+#define VNC_MSG_CLIENT_SET_ENCODINGS 2
+#define VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST 3
+#define VNC_MSG_CLIENT_KEY_EVENT 4
+#define VNC_MSG_CLIENT_POINTER_EVENT 5
+#define VNC_MSG_CLIENT_CUT_TEXT 6
+#define VNC_MSG_CLIENT_VMWARE_0 127
+#define VNC_MSG_CLIENT_CALL_CONTROL 249
+#define VNC_MSG_CLIENT_XVP 250
+#define VNC_MSG_CLIENT_SET_DESKTOP_SIZE 251
+#define VNC_MSG_CLIENT_TIGHT 252
+#define VNC_MSG_CLIENT_GII 253
+#define VNC_MSG_CLIENT_VMWARE_1 254
+#define VNC_MSG_CLIENT_QEMU 255
+
+/* Server -> Client message IDs */
+#define VNC_MSG_SERVER_FRAMEBUFFER_UPDATE 0
+#define VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES 1
+#define VNC_MSG_SERVER_BELL 2
+#define VNC_MSG_SERVER_CUT_TEXT 3
+#define VNC_MSG_SERVER_VMWARE_0 127
+#define VNC_MSG_SERVER_CALL_CONTROL 249
+#define VNC_MSG_SERVER_XVP 250
+#define VNC_MSG_SERVER_TIGHT 252
+#define VNC_MSG_SERVER_GII 253
+#define VNC_MSG_SERVER_VMWARE_1 254
+#define VNC_MSG_SERVER_QEMU 255
+
+
+
+/* QEMU client -> server message IDs */
+#define VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT 0
+#define VNC_MSG_CLIENT_QEMU_AUDIO 1
+
+/* QEMU server -> client message IDs */
+#define VNC_MSG_SERVER_QEMU_AUDIO 1
+
+
+
+/* QEMU client -> server audio message IDs */
+#define VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE 0
+#define VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE 1
+#define VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT 2
+
+/* QEMU server -> client audio message IDs */
+#define VNC_MSG_SERVER_QEMU_AUDIO_END 0
+#define VNC_MSG_SERVER_QEMU_AUDIO_BEGIN 1
+#define VNC_MSG_SERVER_QEMU_AUDIO_DATA 2
+
+
+/*****************************************************************************
+ *
+ * Internal APIs
+ *
+ *****************************************************************************/
+
+/* Event loop functions */
+void vnc_client_read(void *opaque);
+void vnc_client_write(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);
+
+/* Protocol I/O functions */
+void vnc_write(VncState *vs, const void *data, size_t len);
+void vnc_write_u32(VncState *vs, uint32_t value);
+void vnc_write_s32(VncState *vs, int32_t value);
+void vnc_write_u16(VncState *vs, uint16_t value);
+void vnc_write_u8(VncState *vs, uint8_t value);
+void vnc_flush(VncState *vs);
+void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting);
+
+
+/* Buffer I/O functions */
+uint8_t read_u8(uint8_t *data, size_t offset);
+uint16_t read_u16(uint8_t *data, size_t offset);
+int32_t read_s32(uint8_t *data, size_t offset);
+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);
+
+void start_client_init(VncState *vs);
+void start_auth_vnc(VncState *vs);
+
+/* Buffer management */
+void buffer_reserve(Buffer *buffer, size_t len);
+int buffer_empty(Buffer *buffer);
+uint8_t *buffer_end(Buffer *buffer);
+void buffer_reset(Buffer *buffer);
+void buffer_free(Buffer *buffer);
+void buffer_append(Buffer *buffer, const void *data, size_t len);
+
+
+/* Misc helpers */
+
+char *vnc_socket_local_addr(const char *format, int fd);
+char *vnc_socket_remote_addr(const char *format, int fd);
+
+/* Framebuffer */
+void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+ int32_t encoding);
+
+void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v);
+
+/* Encodings */
+int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+
+int vnc_hextile_send_framebuffer_update(VncState *vs, int x,
+ int y, int w, int h);
+void vnc_hextile_set_pixel_conversion(VncState *vs, int generic);
+
+void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size);
+void vnc_zlib_zfree(void *x, void *addr);
+int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+void vnc_zlib_clear(VncState *vs);
+
+
+int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h);
+void vnc_tight_clear(VncState *vs);
+
+#endif /* __QEMU_VNC_H */
diff --git a/ui/vnc_keysym.h b/ui/vnc_keysym.h
new file mode 100644
index 0000000000..55cb87edec
--- /dev/null
+++ b/ui/vnc_keysym.h
@@ -0,0 +1,324 @@
+
+#include "keymaps.h"
+
+static const name2keysym_t name2keysym[]={
+/* ascii */
+ { "space", 0x020},
+ { "exclam", 0x021},
+ { "quotedbl", 0x022},
+ { "numbersign", 0x023},
+ { "dollar", 0x024},
+ { "percent", 0x025},
+ { "ampersand", 0x026},
+ { "apostrophe", 0x027},
+ { "parenleft", 0x028},
+ { "parenright", 0x029},
+ { "asterisk", 0x02a},
+ { "plus", 0x02b},
+ { "comma", 0x02c},
+ { "minus", 0x02d},
+ { "period", 0x02e},
+ { "slash", 0x02f},
+ { "0", 0x030},
+ { "1", 0x031},
+ { "2", 0x032},
+ { "3", 0x033},
+ { "4", 0x034},
+ { "5", 0x035},
+ { "6", 0x036},
+ { "7", 0x037},
+ { "8", 0x038},
+ { "9", 0x039},
+ { "colon", 0x03a},
+ { "semicolon", 0x03b},
+ { "less", 0x03c},
+ { "equal", 0x03d},
+ { "greater", 0x03e},
+ { "question", 0x03f},
+ { "at", 0x040},
+ { "A", 0x041},
+ { "B", 0x042},
+ { "C", 0x043},
+ { "D", 0x044},
+ { "E", 0x045},
+ { "F", 0x046},
+ { "G", 0x047},
+ { "H", 0x048},
+ { "I", 0x049},
+ { "J", 0x04a},
+ { "K", 0x04b},
+ { "L", 0x04c},
+ { "M", 0x04d},
+ { "N", 0x04e},
+ { "O", 0x04f},
+ { "P", 0x050},
+ { "Q", 0x051},
+ { "R", 0x052},
+ { "S", 0x053},
+ { "T", 0x054},
+ { "U", 0x055},
+ { "V", 0x056},
+ { "W", 0x057},
+ { "X", 0x058},
+ { "Y", 0x059},
+ { "Z", 0x05a},
+ { "bracketleft", 0x05b},
+ { "backslash", 0x05c},
+ { "bracketright", 0x05d},
+ { "asciicircum", 0x05e},
+ { "underscore", 0x05f},
+ { "grave", 0x060},
+ { "a", 0x061},
+ { "b", 0x062},
+ { "c", 0x063},
+ { "d", 0x064},
+ { "e", 0x065},
+ { "f", 0x066},
+ { "g", 0x067},
+ { "h", 0x068},
+ { "i", 0x069},
+ { "j", 0x06a},
+ { "k", 0x06b},
+ { "l", 0x06c},
+ { "m", 0x06d},
+ { "n", 0x06e},
+ { "o", 0x06f},
+ { "p", 0x070},
+ { "q", 0x071},
+ { "r", 0x072},
+ { "s", 0x073},
+ { "t", 0x074},
+ { "u", 0x075},
+ { "v", 0x076},
+ { "w", 0x077},
+ { "x", 0x078},
+ { "y", 0x079},
+ { "z", 0x07a},
+ { "braceleft", 0x07b},
+ { "bar", 0x07c},
+ { "braceright", 0x07d},
+ { "asciitilde", 0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace", 0x0a0},
+{ "exclamdown", 0x0a1},
+{ "cent", 0x0a2},
+{ "sterling", 0x0a3},
+{ "currency", 0x0a4},
+{ "yen", 0x0a5},
+{ "brokenbar", 0x0a6},
+{ "section", 0x0a7},
+{ "diaeresis", 0x0a8},
+{ "copyright", 0x0a9},
+{ "ordfeminine", 0x0aa},
+{ "guillemotleft", 0x0ab},
+{ "notsign", 0x0ac},
+{ "hyphen", 0x0ad},
+{ "registered", 0x0ae},
+{ "macron", 0x0af},
+{ "degree", 0x0b0},
+{ "plusminus", 0x0b1},
+{ "twosuperior", 0x0b2},
+{ "threesuperior", 0x0b3},
+{ "acute", 0x0b4},
+{ "mu", 0x0b5},
+{ "paragraph", 0x0b6},
+{ "periodcentered", 0x0b7},
+{ "cedilla", 0x0b8},
+{ "onesuperior", 0x0b9},
+{ "masculine", 0x0ba},
+{ "guillemotright", 0x0bb},
+{ "onequarter", 0x0bc},
+{ "onehalf", 0x0bd},
+{ "threequarters", 0x0be},
+{ "questiondown", 0x0bf},
+{ "Agrave", 0x0c0},
+{ "Aacute", 0x0c1},
+{ "Acircumflex", 0x0c2},
+{ "Atilde", 0x0c3},
+{ "Adiaeresis", 0x0c4},
+{ "Aring", 0x0c5},
+{ "AE", 0x0c6},
+{ "Ccedilla", 0x0c7},
+{ "Egrave", 0x0c8},
+{ "Eacute", 0x0c9},
+{ "Ecircumflex", 0x0ca},
+{ "Ediaeresis", 0x0cb},
+{ "Igrave", 0x0cc},
+{ "Iacute", 0x0cd},
+{ "Icircumflex", 0x0ce},
+{ "Idiaeresis", 0x0cf},
+{ "ETH", 0x0d0},
+{ "Eth", 0x0d0},
+{ "Ntilde", 0x0d1},
+{ "Ograve", 0x0d2},
+{ "Oacute", 0x0d3},
+{ "Ocircumflex", 0x0d4},
+{ "Otilde", 0x0d5},
+{ "Odiaeresis", 0x0d6},
+{ "multiply", 0x0d7},
+{ "Ooblique", 0x0d8},
+{ "Oslash", 0x0d8},
+{ "Ugrave", 0x0d9},
+{ "Uacute", 0x0da},
+{ "Ucircumflex", 0x0db},
+{ "Udiaeresis", 0x0dc},
+{ "Yacute", 0x0dd},
+{ "THORN", 0x0de},
+{ "Thorn", 0x0de},
+{ "ssharp", 0x0df},
+{ "agrave", 0x0e0},
+{ "aacute", 0x0e1},
+{ "acircumflex", 0x0e2},
+{ "atilde", 0x0e3},
+{ "adiaeresis", 0x0e4},
+{ "aring", 0x0e5},
+{ "ae", 0x0e6},
+{ "ccedilla", 0x0e7},
+{ "egrave", 0x0e8},
+{ "eacute", 0x0e9},
+{ "ecircumflex", 0x0ea},
+{ "ediaeresis", 0x0eb},
+{ "igrave", 0x0ec},
+{ "iacute", 0x0ed},
+{ "icircumflex", 0x0ee},
+{ "idiaeresis", 0x0ef},
+{ "eth", 0x0f0},
+{ "ntilde", 0x0f1},
+{ "ograve", 0x0f2},
+{ "oacute", 0x0f3},
+{ "ocircumflex", 0x0f4},
+{ "otilde", 0x0f5},
+{ "odiaeresis", 0x0f6},
+{ "division", 0x0f7},
+{ "oslash", 0x0f8},
+{ "ooblique", 0x0f8},
+{ "ugrave", 0x0f9},
+{ "uacute", 0x0fa},
+{ "ucircumflex", 0x0fb},
+{ "udiaeresis", 0x0fc},
+{ "yacute", 0x0fd},
+{ "thorn", 0x0fe},
+{ "ydiaeresis", 0x0ff},
+{"EuroSign", 0x20ac}, /* XK_EuroSign */
+
+ /* modifiers */
+{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */
+{"Control_L", 0xffe3}, /* XK_Control_L */
+{"Control_R", 0xffe4}, /* XK_Control_R */
+{"Alt_L", 0xffe9}, /* XK_Alt_L */
+{"Alt_R", 0xffea}, /* XK_Alt_R */
+{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */
+{"Meta_L", 0xffe7}, /* XK_Meta_L */
+{"Meta_R", 0xffe8}, /* XK_Meta_R */
+{"Shift_L", 0xffe1}, /* XK_Shift_L */
+{"Shift_R", 0xffe2}, /* XK_Shift_R */
+{"Super_L", 0xffeb}, /* XK_Super_L */
+{"Super_R", 0xffec}, /* XK_Super_R */
+
+ /* special keys */
+{"BackSpace", 0xff08}, /* XK_BackSpace */
+{"Tab", 0xff09}, /* XK_Tab */
+{"Return", 0xff0d}, /* XK_Return */
+{"Right", 0xff53}, /* XK_Right */
+{"Left", 0xff51}, /* XK_Left */
+{"Up", 0xff52}, /* XK_Up */
+{"Down", 0xff54}, /* XK_Down */
+{"Page_Down", 0xff56}, /* XK_Page_Down */
+{"Page_Up", 0xff55}, /* XK_Page_Up */
+{"Insert", 0xff63}, /* XK_Insert */
+{"Delete", 0xffff}, /* XK_Delete */
+{"Home", 0xff50}, /* XK_Home */
+{"End", 0xff57}, /* XK_End */
+{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"KP_Home", 0xff95},
+{"KP_Left", 0xff96},
+{"KP_Up", 0xff97},
+{"KP_Right", 0xff98},
+{"KP_Down", 0xff99},
+{"KP_Prior", 0xff9a},
+{"KP_Page_Up", 0xff9a},
+{"KP_Next", 0xff9b},
+{"KP_Page_Down", 0xff9b},
+{"KP_End", 0xff9c},
+{"KP_Begin", 0xff9d},
+{"KP_Insert", 0xff9e},
+{"KP_Delete", 0xff9f},
+{"F1", 0xffbe}, /* XK_F1 */
+{"F2", 0xffbf}, /* XK_F2 */
+{"F3", 0xffc0}, /* XK_F3 */
+{"F4", 0xffc1}, /* XK_F4 */
+{"F5", 0xffc2}, /* XK_F5 */
+{"F6", 0xffc3}, /* XK_F6 */
+{"F7", 0xffc4}, /* XK_F7 */
+{"F8", 0xffc5}, /* XK_F8 */
+{"F9", 0xffc6}, /* XK_F9 */
+{"F10", 0xffc7}, /* XK_F10 */
+{"F11", 0xffc8}, /* XK_F11 */
+{"F12", 0xffc9}, /* XK_F12 */
+{"F13", 0xffca}, /* XK_F13 */
+{"F14", 0xffcb}, /* XK_F14 */
+{"F15", 0xffcc}, /* XK_F15 */
+{"Sys_Req", 0xff15}, /* XK_Sys_Req */
+{"KP_0", 0xffb0}, /* XK_KP_0 */
+{"KP_1", 0xffb1}, /* XK_KP_1 */
+{"KP_2", 0xffb2}, /* XK_KP_2 */
+{"KP_3", 0xffb3}, /* XK_KP_3 */
+{"KP_4", 0xffb4}, /* XK_KP_4 */
+{"KP_5", 0xffb5}, /* XK_KP_5 */
+{"KP_6", 0xffb6}, /* XK_KP_6 */
+{"KP_7", 0xffb7}, /* XK_KP_7 */
+{"KP_8", 0xffb8}, /* XK_KP_8 */
+{"KP_9", 0xffb9}, /* XK_KP_9 */
+{"KP_Add", 0xffab}, /* XK_KP_Add */
+{"KP_Separator", 0xffac},/* XK_KP_Separator */
+{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */
+{"KP_Divide", 0xffaf}, /* XK_KP_Divide */
+{"KP_Enter", 0xff8d}, /* XK_KP_Enter */
+{"KP_Equal", 0xffbd}, /* XK_KP_Equal */
+{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */
+{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */
+{"help", 0xff6a}, /* XK_Help */
+{"Menu", 0xff67}, /* XK_Menu */
+{"Print", 0xff61}, /* XK_Print */
+{"Mode_switch", 0xff7e}, /* XK_Mode_switch */
+{"Num_Lock", 0xff7f}, /* XK_Num_Lock */
+{"Pause", 0xff13}, /* XK_Pause */
+{"Escape", 0xff1b}, /* XK_Escape */
+
+/* dead keys */
+{"dead_grave", 0xfe50}, /* XK_dead_grave */
+{"dead_acute", 0xfe51}, /* XK_dead_acute */
+{"dead_circumflex", 0xfe52}, /* XK_dead_circumflex */
+{"dead_tilde", 0xfe53}, /* XK_dead_tilde */
+{"dead_macron", 0xfe54}, /* XK_dead_macron */
+{"dead_breve", 0xfe55}, /* XK_dead_breve */
+{"dead_abovedot", 0xfe56}, /* XK_dead_abovedot */
+{"dead_diaeresis", 0xfe57}, /* XK_dead_diaeresis */
+{"dead_abovering", 0xfe58}, /* XK_dead_abovering */
+{"dead_doubleacute", 0xfe59}, /* XK_dead_doubleacute */
+{"dead_caron", 0xfe5a}, /* XK_dead_caron */
+{"dead_cedilla", 0xfe5b}, /* XK_dead_cedilla */
+{"dead_ogonek", 0xfe5c}, /* XK_dead_ogonek */
+{"dead_iota", 0xfe5d}, /* XK_dead_iota */
+{"dead_voiced_sound", 0xfe5e}, /* XK_dead_voiced_sound */
+{"dead_semivoiced_sound", 0xfe5f}, /* XK_dead_semivoiced_sound */
+{"dead_belowdot", 0xfe60}, /* XK_dead_belowdot */
+{"dead_hook", 0xfe61}, /* XK_dead_hook */
+{"dead_horn", 0xfe62}, /* XK_dead_horn */
+
+
+ /* localized keys */
+{"BackApostrophe", 0xff21},
+{"Muhenkan", 0xff22},
+{"Katakana", 0xff27},
+{"Hankaku", 0xff29},
+{"Zenkaku_Hankaku", 0xff2a},
+{"Henkan_Mode_Real", 0xff23},
+{"Henkan_Mode_Ultra", 0xff3e},
+{"backslash_ja", 0xffa5},
+{"Katakana_Real", 0xff25},
+{"Eisu_toggle", 0xff30},
+
+{NULL,0},
+};
diff --git a/ui/vnchextile.h b/ui/vnchextile.h
new file mode 100644
index 0000000000..b9f9f5ef89
--- /dev/null
+++ b/ui/vnchextile.h
@@ -0,0 +1,211 @@
+#define CONCAT_I(a, b) a ## b
+#define CONCAT(a, b) CONCAT_I(a, b)
+#define pixel_t CONCAT(uint, CONCAT(BPP, _t))
+#ifdef GENERIC
+#define NAME CONCAT(generic_, BPP)
+#else
+#define NAME BPP
+#endif
+
+static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
+ int x, int y, int w, int h,
+ void *last_bg_,
+ void *last_fg_,
+ int *has_bg, int *has_fg)
+{
+ VncDisplay *vd = vs->vd;
+ uint8_t *row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds);
+ pixel_t *irow = (pixel_t *)row;
+ int j, i;
+ pixel_t *last_bg = (pixel_t *)last_bg_;
+ pixel_t *last_fg = (pixel_t *)last_fg_;
+ pixel_t bg = 0;
+ pixel_t fg = 0;
+ int n_colors = 0;
+ int bg_count = 0;
+ int fg_count = 0;
+ int flags = 0;
+ uint8_t data[(vs->clientds.pf.bytes_per_pixel + 2) * 16 * 16];
+ int n_data = 0;
+ int n_subtiles = 0;
+
+ for (j = 0; j < h; j++) {
+ for (i = 0; i < w; i++) {
+ switch (n_colors) {
+ case 0:
+ bg = irow[i];
+ n_colors = 1;
+ break;
+ case 1:
+ if (irow[i] != bg) {
+ fg = irow[i];
+ n_colors = 2;
+ }
+ break;
+ case 2:
+ if (irow[i] != bg && irow[i] != fg) {
+ n_colors = 3;
+ } else {
+ if (irow[i] == bg)
+ bg_count++;
+ else if (irow[i] == fg)
+ fg_count++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (n_colors > 2)
+ break;
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+
+ if (n_colors > 1 && fg_count > bg_count) {
+ pixel_t tmp = fg;
+ fg = bg;
+ bg = tmp;
+ }
+
+ if (!*has_bg || *last_bg != bg) {
+ flags |= 0x02;
+ *has_bg = 1;
+ *last_bg = bg;
+ }
+
+ if (n_colors < 3 && (!*has_fg || *last_fg != fg)) {
+ flags |= 0x04;
+ *has_fg = 1;
+ *last_fg = fg;
+ }
+
+ switch (n_colors) {
+ case 1:
+ n_data = 0;
+ break;
+ case 2:
+ flags |= 0x08;
+
+ irow = (pixel_t *)row;
+
+ for (j = 0; j < h; j++) {
+ int min_x = -1;
+ for (i = 0; i < w; i++) {
+ if (irow[i] == fg) {
+ if (min_x == -1)
+ min_x = i;
+ } else if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ min_x = -1;
+ }
+ }
+ if (min_x != -1) {
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+ break;
+ case 3:
+ flags |= 0x18;
+
+ irow = (pixel_t *)row;
+
+ if (!*has_bg || *last_bg != bg)
+ flags |= 0x02;
+
+ for (j = 0; j < h; j++) {
+ int has_color = 0;
+ int min_x = -1;
+ pixel_t color = 0; /* shut up gcc */
+
+ for (i = 0; i < w; i++) {
+ if (!has_color) {
+ if (irow[i] == bg)
+ continue;
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ } else if (irow[i] != color) {
+ has_color = 0;
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->clientds.pf.bytes_per_pixel;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+
+ min_x = -1;
+ if (irow[i] != bg) {
+ color = irow[i];
+ min_x = i;
+ has_color = 1;
+ }
+ }
+ }
+ if (has_color) {
+#ifdef GENERIC
+ vnc_convert_pixel(vs, data + n_data, color);
+ n_data += vs->clientds.pf.bytes_per_pixel;
+#else
+ memcpy(data + n_data, &color, sizeof(color));
+ n_data += sizeof(pixel_t);
+#endif
+ hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+ n_data += 2;
+ n_subtiles++;
+ }
+ irow += ds_get_linesize(vs->ds) / sizeof(pixel_t);
+ }
+
+ /* A SubrectsColoured subtile invalidates the foreground color */
+ *has_fg = 0;
+ if (n_data > (w * h * sizeof(pixel_t))) {
+ n_colors = 4;
+ flags = 0x01;
+ *has_bg = 0;
+
+ /* we really don't have to invalidate either the bg or fg
+ but we've lost the old values. oh well. */
+ }
+ default:
+ break;
+ }
+
+ if (n_colors > 3) {
+ flags = 0x01;
+ *has_fg = 0;
+ *has_bg = 0;
+ n_colors = 4;
+ }
+
+ vnc_write_u8(vs, flags);
+ if (n_colors < 4) {
+ if (flags & 0x02)
+ vs->write_pixels(vs, &vd->server->pf, last_bg, sizeof(pixel_t));
+ if (flags & 0x04)
+ vs->write_pixels(vs, &vd->server->pf, last_fg, sizeof(pixel_t));
+ if (n_subtiles) {
+ vnc_write_u8(vs, n_subtiles);
+ vnc_write(vs, data, n_data);
+ }
+ } else {
+ for (j = 0; j < h; j++) {
+ vs->write_pixels(vs, &vd->server->pf, row,
+ w * ds_get_bytes_per_pixel(vs->ds));
+ row += ds_get_linesize(vs->ds);
+ }
+ }
+}
+
+#undef NAME
+#undef pixel_t
+#undef CONCAT_I
+#undef CONCAT
diff --git a/ui/x_keymap.c b/ui/x_keymap.c
new file mode 100644
index 0000000000..b9b0944180
--- /dev/null
+++ b/ui/x_keymap.c
@@ -0,0 +1,168 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "x_keymap.h"
+
+static const uint8_t x_keycode_to_pc_keycode[115] = {
+ 0xc7, /* 97 Home */
+ 0xc8, /* 98 Up */
+ 0xc9, /* 99 PgUp */
+ 0xcb, /* 100 Left */
+ 0x4c, /* 101 KP-5 */
+ 0xcd, /* 102 Right */
+ 0xcf, /* 103 End */
+ 0xd0, /* 104 Down */
+ 0xd1, /* 105 PgDn */
+ 0xd2, /* 106 Ins */
+ 0xd3, /* 107 Del */
+ 0x9c, /* 108 Enter */
+ 0x9d, /* 109 Ctrl-R */
+ 0x0, /* 110 Pause */
+ 0xb7, /* 111 Print */
+ 0xb5, /* 112 Divide */
+ 0xb8, /* 113 Alt-R */
+ 0xc6, /* 114 Break */
+ 0x0, /* 115 */
+ 0x0, /* 116 */
+ 0x0, /* 117 */
+ 0x0, /* 118 */
+ 0x0, /* 119 */
+ 0x0, /* 120 */
+ 0x0, /* 121 */
+ 0x0, /* 122 */
+ 0x0, /* 123 */
+ 0x0, /* 124 */
+ 0x0, /* 125 */
+ 0x0, /* 126 */
+ 0x0, /* 127 */
+ 0x0, /* 128 */
+ 0x79, /* 129 Henkan */
+ 0x0, /* 130 */
+ 0x7b, /* 131 Muhenkan */
+ 0x0, /* 132 */
+ 0x7d, /* 133 Yen */
+ 0x0, /* 134 */
+ 0x0, /* 135 */
+ 0x47, /* 136 KP_7 */
+ 0x48, /* 137 KP_8 */
+ 0x49, /* 138 KP_9 */
+ 0x4b, /* 139 KP_4 */
+ 0x4c, /* 140 KP_5 */
+ 0x4d, /* 141 KP_6 */
+ 0x4f, /* 142 KP_1 */
+ 0x50, /* 143 KP_2 */
+ 0x51, /* 144 KP_3 */
+ 0x52, /* 145 KP_0 */
+ 0x53, /* 146 KP_. */
+ 0x47, /* 147 KP_HOME */
+ 0x48, /* 148 KP_UP */
+ 0x49, /* 149 KP_PgUp */
+ 0x4b, /* 150 KP_Left */
+ 0x4c, /* 151 KP_ */
+ 0x4d, /* 152 KP_Right */
+ 0x4f, /* 153 KP_End */
+ 0x50, /* 154 KP_Down */
+ 0x51, /* 155 KP_PgDn */
+ 0x52, /* 156 KP_Ins */
+ 0x53, /* 157 KP_Del */
+};
+
+/* This table is generated based off the xfree86 -> scancode mapping above
+ * and the keycode mappings in /usr/share/X11/xkb/keycodes/evdev
+ * and /usr/share/X11/xkb/keycodes/xfree86
+ */
+
+static const uint8_t evdev_keycode_to_pc_keycode[61] = {
+ 0, /* 97 EVDEV - RO ("Internet" Keyboards) */
+ 0, /* 98 EVDEV - KATA (Katakana) */
+ 0, /* 99 EVDEV - HIRA (Hiragana) */
+ 0x79, /* 100 EVDEV - HENK (Henkan) */
+ 0x70, /* 101 EVDEV - HKTG (Hiragana/Katakana toggle) */
+ 0x7b, /* 102 EVDEV - MUHE (Muhenkan) */
+ 0, /* 103 EVDEV - JPCM (KPJPComma) */
+ 0x9c, /* 104 KPEN */
+ 0x9d, /* 105 RCTL */
+ 0xb5, /* 106 KPDV */
+ 0xb7, /* 107 PRSC */
+ 0xb8, /* 108 RALT */
+ 0, /* 109 EVDEV - LNFD ("Internet" Keyboards) */
+ 0xc7, /* 110 HOME */
+ 0xc8, /* 111 UP */
+ 0xc9, /* 112 PGUP */
+ 0xcb, /* 113 LEFT */
+ 0xcd, /* 114 RGHT */
+ 0xcf, /* 115 END */
+ 0xd0, /* 116 DOWN */
+ 0xd1, /* 117 PGDN */
+ 0xd2, /* 118 INS */
+ 0xd3, /* 119 DELE */
+ 0, /* 120 EVDEV - I120 ("Internet" Keyboards) */
+ 0, /* 121 EVDEV - MUTE */
+ 0, /* 122 EVDEV - VOL- */
+ 0, /* 123 EVDEV - VOL+ */
+ 0, /* 124 EVDEV - POWR */
+ 0, /* 125 EVDEV - KPEQ */
+ 0, /* 126 EVDEV - I126 ("Internet" Keyboards) */
+ 0, /* 127 EVDEV - PAUS */
+ 0, /* 128 EVDEV - ???? */
+ 0, /* 129 EVDEV - I129 ("Internet" Keyboards) */
+ 0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
+ 0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
+ 0x7d, /* 132 AE13 (Yen)*/
+ 0xdb, /* 133 EVDEV - LWIN */
+ 0xdc, /* 134 EVDEV - RWIN */
+ 0xdd, /* 135 EVDEV - MENU */
+ 0, /* 136 EVDEV - STOP */
+ 0, /* 137 EVDEV - AGAI */
+ 0, /* 138 EVDEV - PROP */
+ 0, /* 139 EVDEV - UNDO */
+ 0, /* 140 EVDEV - FRNT */
+ 0, /* 141 EVDEV - COPY */
+ 0, /* 142 EVDEV - OPEN */
+ 0, /* 143 EVDEV - PAST */
+ 0, /* 144 EVDEV - FIND */
+ 0, /* 145 EVDEV - CUT */
+ 0, /* 146 EVDEV - HELP */
+ 0, /* 147 EVDEV - I147 */
+ 0, /* 148 EVDEV - I148 */
+ 0, /* 149 EVDEV - I149 */
+ 0, /* 150 EVDEV - I150 */
+ 0, /* 151 EVDEV - I151 */
+ 0, /* 152 EVDEV - I152 */
+ 0, /* 153 EVDEV - I153 */
+ 0, /* 154 EVDEV - I154 */
+ 0, /* 155 EVDEV - I156 */
+ 0, /* 156 EVDEV - I157 */
+ 0, /* 157 EVDEV - I158 */
+};
+
+uint8_t translate_xfree86_keycode(const int key)
+{
+ return x_keycode_to_pc_keycode[key];
+}
+
+uint8_t translate_evdev_keycode(const int key)
+{
+ return evdev_keycode_to_pc_keycode[key];
+}
diff --git a/ui/x_keymap.h b/ui/x_keymap.h
new file mode 100644
index 0000000000..2042ce0ed2
--- /dev/null
+++ b/ui/x_keymap.h
@@ -0,0 +1,32 @@
+/*
+ * QEMU SDL display driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_X_KEYMAP_H
+#define QEMU_X_KEYMAP_H
+
+extern uint8_t translate_xfree86_keycode(const int key);
+
+extern uint8_t translate_evdev_keycode(const int key);
+
+#endif