xref: /kvmtool/ui/vnc.c (revision b6f60a8557900d51ec7c9920f2fd964e50dd9023)
1aba1efa5SPekka Enberg #include "kvm/vnc.h"
2aba1efa5SPekka Enberg 
3aba1efa5SPekka Enberg #include "kvm/framebuffer.h"
4aba1efa5SPekka Enberg #include "kvm/i8042.h"
5aba1efa5SPekka Enberg 
6aba1efa5SPekka Enberg #include <linux/types.h>
7*b6f60a85SPekka Enberg #include <rfb/keysym.h>
8aba1efa5SPekka Enberg #include <rfb/rfb.h>
9aba1efa5SPekka Enberg #include <pthread.h>
10aba1efa5SPekka Enberg 
11aba1efa5SPekka Enberg #define VESA_QUEUE_SIZE		128
12aba1efa5SPekka Enberg #define VESA_IRQ		14
13aba1efa5SPekka Enberg 
14aba1efa5SPekka Enberg /*
15aba1efa5SPekka Enberg  * This "6000" value is pretty much the result of experimentation
16aba1efa5SPekka Enberg  * It seems that around this value, things update pretty smoothly
17aba1efa5SPekka Enberg  */
18aba1efa5SPekka Enberg #define VESA_UPDATE_TIME	6000
19aba1efa5SPekka Enberg 
20*b6f60a85SPekka Enberg /*
21*b6f60a85SPekka Enberg  * We can map the letters and numbers without a fuss,
22*b6f60a85SPekka Enberg  * but the other characters not so much.
23*b6f60a85SPekka Enberg  */
24*b6f60a85SPekka Enberg static char letters[26] = {
25*b6f60a85SPekka Enberg 	0x1c, 0x32, 0x21, 0x23, 0x24, /* a-e */
26*b6f60a85SPekka Enberg 	0x2b, 0x34, 0x33, 0x43, 0x3b, /* f-j */
27*b6f60a85SPekka Enberg 	0x42, 0x4b, 0x3a, 0x31, 0x44, /* k-o */
28*b6f60a85SPekka Enberg 	0x4d, 0x15, 0x2d, 0x1b, 0x2c, /* p-t */
29*b6f60a85SPekka Enberg 	0x3c, 0x2a, 0x1d, 0x22, 0x35, /* u-y */
30*b6f60a85SPekka Enberg 	0x1a,
31*b6f60a85SPekka Enberg };
32*b6f60a85SPekka Enberg 
33*b6f60a85SPekka Enberg static char num[10] = {
34*b6f60a85SPekka Enberg 	0x45, 0x16, 0x1e, 0x26, 0x2e, 0x23, 0x36, 0x3d, 0x3e, 0x46,
35*b6f60a85SPekka Enberg };
36*b6f60a85SPekka Enberg 
37*b6f60a85SPekka Enberg /*
38*b6f60a85SPekka Enberg  * This is called when the VNC server receives a key event
39*b6f60a85SPekka Enberg  * The reason this function is such a beast is that we have
40*b6f60a85SPekka Enberg  * to convert from ASCII characters (which is what VNC gets)
41*b6f60a85SPekka Enberg  * to PC keyboard scancodes, which is what Linux expects to
42*b6f60a85SPekka Enberg  * get from its keyboard. ASCII and the scancode set don't
43*b6f60a85SPekka Enberg  * really seem to mesh in any good way beyond some basics with
44*b6f60a85SPekka Enberg  * the letters and numbers.
45*b6f60a85SPekka Enberg  */
46*b6f60a85SPekka Enberg static void kbd_handle_key(rfbBool down, rfbKeySym key, rfbClientPtr cl)
47*b6f60a85SPekka Enberg {
48*b6f60a85SPekka Enberg 	char tosend = 0;
49*b6f60a85SPekka Enberg 
50*b6f60a85SPekka Enberg 	if (key >= 0x41 && key <= 0x5a)
51*b6f60a85SPekka Enberg 		key += 0x20; /* convert to lowercase */
52*b6f60a85SPekka Enberg 
53*b6f60a85SPekka Enberg 	if (key >= 0x61 && key <= 0x7a) /* a-z */
54*b6f60a85SPekka Enberg 		tosend = letters[key - 0x61];
55*b6f60a85SPekka Enberg 
56*b6f60a85SPekka Enberg 	if (key >= 0x30 && key <= 0x39)
57*b6f60a85SPekka Enberg 		tosend = num[key - 0x30];
58*b6f60a85SPekka Enberg 
59*b6f60a85SPekka Enberg 	switch (key) {
60*b6f60a85SPekka Enberg 	case XK_Insert:		kbd_queue(0xe0);	tosend = 0x70;	break;
61*b6f60a85SPekka Enberg 	case XK_Delete:		kbd_queue(0xe0);	tosend = 0x71;	break;
62*b6f60a85SPekka Enberg 	case XK_Up:		kbd_queue(0xe0);	tosend = 0x75;	break;
63*b6f60a85SPekka Enberg 	case XK_Down:		kbd_queue(0xe0);	tosend = 0x72;	break;
64*b6f60a85SPekka Enberg 	case XK_Left:		kbd_queue(0xe0);	tosend = 0x6b;	break;
65*b6f60a85SPekka Enberg 	case XK_Right:		kbd_queue(0xe0);	tosend = 0x74;	break;
66*b6f60a85SPekka Enberg 	case XK_Page_Up:	kbd_queue(0xe0);	tosend = 0x7d;	break;
67*b6f60a85SPekka Enberg 	case XK_Page_Down:	kbd_queue(0xe0);	tosend = 0x7a;	break;
68*b6f60a85SPekka Enberg 	case XK_Home:		kbd_queue(0xe0);	tosend = 0x6c;	break;
69*b6f60a85SPekka Enberg 	case XK_BackSpace:	tosend = 0x66;		break;
70*b6f60a85SPekka Enberg 	case XK_Tab:		tosend = 0x0d;		break;
71*b6f60a85SPekka Enberg 	case XK_Return:		tosend = 0x5a;		break;
72*b6f60a85SPekka Enberg 	case XK_Escape:		tosend = 0x76;		break;
73*b6f60a85SPekka Enberg 	case XK_End:		tosend = 0x69;		break;
74*b6f60a85SPekka Enberg 	case XK_Shift_L:	tosend = 0x12;		break;
75*b6f60a85SPekka Enberg 	case XK_Shift_R:	tosend = 0x59;		break;
76*b6f60a85SPekka Enberg 	case XK_Control_R:	kbd_queue(0xe0);
77*b6f60a85SPekka Enberg 	case XK_Control_L:	tosend = 0x14;		break;
78*b6f60a85SPekka Enberg 	case XK_Alt_R:		kbd_queue(0xe0);
79*b6f60a85SPekka Enberg 	case XK_Alt_L:		tosend = 0x11;		break;
80*b6f60a85SPekka Enberg 	case XK_quoteleft:	tosend = 0x0e;		break;
81*b6f60a85SPekka Enberg 	case XK_minus:		tosend = 0x4e;		break;
82*b6f60a85SPekka Enberg 	case XK_equal:		tosend = 0x55;		break;
83*b6f60a85SPekka Enberg 	case XK_bracketleft:	tosend = 0x54;		break;
84*b6f60a85SPekka Enberg 	case XK_bracketright:	tosend = 0x5b;		break;
85*b6f60a85SPekka Enberg 	case XK_backslash:	tosend = 0x5d;		break;
86*b6f60a85SPekka Enberg 	case XK_Caps_Lock:	tosend = 0x58;		break;
87*b6f60a85SPekka Enberg 	case XK_semicolon:	tosend = 0x4c;		break;
88*b6f60a85SPekka Enberg 	case XK_quoteright:	tosend = 0x52;		break;
89*b6f60a85SPekka Enberg 	case XK_comma:		tosend = 0x41;		break;
90*b6f60a85SPekka Enberg 	case XK_period:		tosend = 0x49;		break;
91*b6f60a85SPekka Enberg 	case XK_slash:		tosend = 0x4a;		break;
92*b6f60a85SPekka Enberg 	case XK_space:		tosend = 0x29;		break;
93*b6f60a85SPekka Enberg 
94*b6f60a85SPekka Enberg 	/*
95*b6f60a85SPekka Enberg 	 * This is where I handle the shifted characters.
96*b6f60a85SPekka Enberg 	 * They don't really map nicely the way A-Z maps to a-z,
97*b6f60a85SPekka Enberg 	 * so I'm doing it manually
98*b6f60a85SPekka Enberg 	 */
99*b6f60a85SPekka Enberg 	case XK_exclam:		tosend = 0x16;		break;
100*b6f60a85SPekka Enberg 	case XK_quotedbl:	tosend = 0x52;		break;
101*b6f60a85SPekka Enberg 	case XK_numbersign:	tosend = 0x26;		break;
102*b6f60a85SPekka Enberg 	case XK_dollar:		tosend = 0x25;		break;
103*b6f60a85SPekka Enberg 	case XK_percent:	tosend = 0x2e;		break;
104*b6f60a85SPekka Enberg 	case XK_ampersand:	tosend = 0x3d;		break;
105*b6f60a85SPekka Enberg 	case XK_parenleft:	tosend = 0x46;		break;
106*b6f60a85SPekka Enberg 	case XK_parenright:	tosend = 0x45;		break;
107*b6f60a85SPekka Enberg 	case XK_asterisk:	tosend = 0x3e;		break;
108*b6f60a85SPekka Enberg 	case XK_plus:		tosend = 0x55;		break;
109*b6f60a85SPekka Enberg 	case XK_colon:		tosend = 0x4c;		break;
110*b6f60a85SPekka Enberg 	case XK_less:		tosend = 0x41;		break;
111*b6f60a85SPekka Enberg 	case XK_greater:	tosend = 0x49;		break;
112*b6f60a85SPekka Enberg 	case XK_question:	tosend = 0x4a;		break;
113*b6f60a85SPekka Enberg 	case XK_at:		tosend = 0x1e;		break;
114*b6f60a85SPekka Enberg 	case XK_asciicircum:	tosend = 0x36;		break;
115*b6f60a85SPekka Enberg 	case XK_underscore:	tosend = 0x4e;		break;
116*b6f60a85SPekka Enberg 	case XK_braceleft:	tosend = 0x54;		break;
117*b6f60a85SPekka Enberg 	case XK_braceright:	tosend = 0x5b;		break;
118*b6f60a85SPekka Enberg 	case XK_bar:		tosend = 0x5d;		break;
119*b6f60a85SPekka Enberg 	case XK_asciitilde:	tosend = 0x0e;		break;
120*b6f60a85SPekka Enberg 	default:		break;
121*b6f60a85SPekka Enberg 	}
122*b6f60a85SPekka Enberg 
123*b6f60a85SPekka Enberg 	/*
124*b6f60a85SPekka Enberg 	 * If this is a "key up" event (the user has released the key, we
125*b6f60a85SPekka Enberg 	 * need to send 0xf0 first.
126*b6f60a85SPekka Enberg 	 */
127*b6f60a85SPekka Enberg 	if (!down && tosend != 0x0)
128*b6f60a85SPekka Enberg 		kbd_queue(0xf0);
129*b6f60a85SPekka Enberg 
130*b6f60a85SPekka Enberg 	if (tosend)
131*b6f60a85SPekka Enberg 		kbd_queue(tosend);
132*b6f60a85SPekka Enberg }
133*b6f60a85SPekka Enberg 
134*b6f60a85SPekka Enberg /* The previous X and Y coordinates of the mouse */
135*b6f60a85SPekka Enberg static int xlast, ylast = -1;
136*b6f60a85SPekka Enberg 
137*b6f60a85SPekka Enberg /*
138*b6f60a85SPekka Enberg  * This function is called by the VNC server whenever a mouse event occurs.
139*b6f60a85SPekka Enberg  */
140*b6f60a85SPekka Enberg static void kbd_handle_ptr(int buttonMask, int x, int y, rfbClientPtr cl)
141*b6f60a85SPekka Enberg {
142*b6f60a85SPekka Enberg 	int dx, dy;
143*b6f60a85SPekka Enberg 	char b1 = 0x8;
144*b6f60a85SPekka Enberg 
145*b6f60a85SPekka Enberg 	/* The VNC mask and the PS/2 button encoding are the same */
146*b6f60a85SPekka Enberg 	b1 |= buttonMask;
147*b6f60a85SPekka Enberg 
148*b6f60a85SPekka Enberg 	if (xlast >= 0 && ylast >= 0) {
149*b6f60a85SPekka Enberg 		/* The PS/2 mouse sends deltas, not absolutes */
150*b6f60a85SPekka Enberg 		dx = x - xlast;
151*b6f60a85SPekka Enberg 		dy = ylast - y;
152*b6f60a85SPekka Enberg 
153*b6f60a85SPekka Enberg 		/* Set overflow bits if needed */
154*b6f60a85SPekka Enberg 		if (dy > 255)
155*b6f60a85SPekka Enberg 			b1 |= 0x80;
156*b6f60a85SPekka Enberg 		if (dx > 255)
157*b6f60a85SPekka Enberg 			b1 |= 0x40;
158*b6f60a85SPekka Enberg 
159*b6f60a85SPekka Enberg 		/* Set negative bits if needed */
160*b6f60a85SPekka Enberg 		if (dy < 0)
161*b6f60a85SPekka Enberg 			b1 |= 0x20;
162*b6f60a85SPekka Enberg 		if (dx < 0)
163*b6f60a85SPekka Enberg 			b1 |= 0x10;
164*b6f60a85SPekka Enberg 
165*b6f60a85SPekka Enberg 		mouse_queue(b1);
166*b6f60a85SPekka Enberg 		mouse_queue(dx);
167*b6f60a85SPekka Enberg 		mouse_queue(dy);
168*b6f60a85SPekka Enberg 	}
169*b6f60a85SPekka Enberg 
170*b6f60a85SPekka Enberg 	xlast = x;
171*b6f60a85SPekka Enberg 	ylast = y;
172*b6f60a85SPekka Enberg 	rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
173*b6f60a85SPekka Enberg }
174*b6f60a85SPekka Enberg 
175aba1efa5SPekka Enberg static void vnc__write(struct framebuffer *fb, u64 addr, u8 *data, u32 len)
176aba1efa5SPekka Enberg {
177aba1efa5SPekka Enberg 	memcpy(&fb->mem[addr - fb->mem_addr], data, len);
178aba1efa5SPekka Enberg }
179aba1efa5SPekka Enberg 
180aba1efa5SPekka Enberg static void *vnc__thread(void *p)
181aba1efa5SPekka Enberg {
182aba1efa5SPekka Enberg 	struct framebuffer *fb = p;
183aba1efa5SPekka Enberg 	/*
184aba1efa5SPekka Enberg 	 * Make a fake argc and argv because the getscreen function
185aba1efa5SPekka Enberg 	 * seems to want it.
186aba1efa5SPekka Enberg 	 */
187aba1efa5SPekka Enberg 	char argv[1][1] = {{0}};
188aba1efa5SPekka Enberg 	int argc = 1;
189aba1efa5SPekka Enberg 
190aba1efa5SPekka Enberg 	rfbScreenInfoPtr server;
191aba1efa5SPekka Enberg 
192aba1efa5SPekka Enberg 	server = rfbGetScreen(&argc, (char **) argv, fb->width, fb->height, 8, 3, 4);
193aba1efa5SPekka Enberg 	server->frameBuffer		= fb->mem;
194aba1efa5SPekka Enberg 	server->alwaysShared		= TRUE;
195aba1efa5SPekka Enberg 	server->kbdAddEvent		= kbd_handle_key;
196aba1efa5SPekka Enberg 	server->ptrAddEvent		= kbd_handle_ptr;
197aba1efa5SPekka Enberg 	rfbInitServer(server);
198aba1efa5SPekka Enberg 
199aba1efa5SPekka Enberg 	while (rfbIsActive(server)) {
200aba1efa5SPekka Enberg 		rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height);
201aba1efa5SPekka Enberg 		rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
202aba1efa5SPekka Enberg 	}
203aba1efa5SPekka Enberg 	return NULL;
204aba1efa5SPekka Enberg }
205aba1efa5SPekka Enberg 
206aba1efa5SPekka Enberg static int vnc__start(struct framebuffer *fb)
207aba1efa5SPekka Enberg {
208aba1efa5SPekka Enberg 	pthread_t thread;
209aba1efa5SPekka Enberg 
210aba1efa5SPekka Enberg 	if (pthread_create(&thread, NULL, vnc__thread, fb) != 0)
211aba1efa5SPekka Enberg 		return -1;
212aba1efa5SPekka Enberg 
213aba1efa5SPekka Enberg 	return 0;
214aba1efa5SPekka Enberg }
215aba1efa5SPekka Enberg 
216aba1efa5SPekka Enberg static struct fb_target_operations vnc_ops = {
217aba1efa5SPekka Enberg 	.start			= vnc__start,
218aba1efa5SPekka Enberg 	.write			= vnc__write,
219aba1efa5SPekka Enberg };
220aba1efa5SPekka Enberg 
221aba1efa5SPekka Enberg void vnc__init(struct framebuffer *fb)
222aba1efa5SPekka Enberg {
223aba1efa5SPekka Enberg 	fb__attach(fb, &vnc_ops);
224aba1efa5SPekka Enberg }
225