1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <stdint.h>
6 #include <stdbool.h>
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <stddef.h>
10 #include <pthread.h>
11 #include <poll.h>
12 #include <errno.h>
13 
14 /*
15  * some ports for vncserver install endian.h that push out an annoying warning
16  */
17 #ifdef __APPLE__
18 #define APPLE
19 #endif
20 #include <rfb/rfbclient.h>
21 #include <rfb/rfb.h>
22 
23 #include "arcan_shmif.h"
24 #include "frameserver.h"
25 #include "xsymconv.h"
26 
27 /*
28  * missing:
29  *  cut and paste (GotXCutTextProc), sending receiving files
30  *  connecting through incoming descriptor
31  *  labels
32  *  xvb -> force reset (extension)
33  *  ExtendedDesktopSize -> respond to displayhints with SetDesktopSize
34  *  HandleTextChatProc
35  *  FinishedFramebuffferUpdateProc
36  *  rfbRegisterTightVNCFileTransferExtension()
37  *  desktopName propagation
38  *  specifyEncodingType
39  */
40 
41 static struct {
42 	struct arcan_shmif_cont shmcont;
43 	char* pass;
44 	int depth;
45 	rfbClient* client;
46 	bool dirty, forcealpha;
47 } vncctx = {0};
48 
49 int mouse_button_map[] = {
50 	rfbButton1Mask,
51 	rfbButton3Mask,
52 	rfbButton2Mask,
53 	rfbButton4Mask,
54 	rfbButton5Mask
55 };
56 
client_resize(struct _rfbClient * client)57 static rfbBool client_resize(struct _rfbClient* client)
58 {
59 	int neww = client->width;
60 	int newh = client->height;
61 
62 	vncctx.shmcont.hints = SHMIF_RHINT_SUBREGION | SHMIF_RHINT_IGNORE_ALPHA;
63 
64 	if (!arcan_shmif_resize(&vncctx.shmcont, neww, newh)){
65 		LOG("client requested a resize outside "
66 			"accepted dimensions (%d, %d)\n", neww, newh);
67 		return false;
68 	}
69 	else
70 		LOG("client resize to %d, %d\n", neww, newh);
71 
72 	vncctx.depth = client->format.bitsPerPixel / 8;
73 	client->updateRect.x = 0;
74 	client->updateRect.y = 0;
75 	client->updateRect.w = neww;
76 	client->updateRect.h = newh;
77 	client->format.bitsPerPixel = 32;
78 
79 /* figure out "native" channel position and shift mask */
80 	if (SHMIF_RGBA(0xff, 0x00, 0x00, 0x00) == 0xff000000)
81 		client->format.redShift = 24;
82 	else if (SHMIF_RGBA(0xff, 0x00, 0x00, 0x00) == 0x00ff0000)
83 		client->format.redShift = 16;
84 	else if (SHMIF_RGBA(0xff, 0x00, 0x00, 0x00) == 0x0000ff00)
85 		client->format.redShift = 8;
86 	else
87 		client->format.redShift = 0;
88 
89 	if (SHMIF_RGBA(0x00, 0xff, 0x00, 0x00) == 0xff000000)
90 		client->format.greenShift = 24;
91 	else if (SHMIF_RGBA(0x00, 0xff, 0x00, 0x00) == 0x00ff0000)
92 		client->format.greenShift = 16;
93 	else if (SHMIF_RGBA(0x00, 0xff, 0x00, 0x00) == 0x0000ff00)
94 		client->format.greenShift = 8;
95 	else
96 		client->format.greenShift = 0;
97 
98 	if (SHMIF_RGBA(0x00, 0x00, 0xff, 0x00) == 0xff000000)
99 		client->format.blueShift = 24;
100 	else if (SHMIF_RGBA(0x00, 0x00, 0xff, 0x00) == 0x00ff0000)
101 		client->format.blueShift = 16;
102 	else if (SHMIF_RGBA(0x00, 0x00, 0xff, 0x00) == 0x0000ff00)
103 		client->format.blueShift = 8;
104 	else
105 		client->format.blueShift = 0;
106 
107 	client->frameBuffer = (uint8_t*) vncctx.shmcont.vidp;
108 	SetFormatAndEncodings(client);
109 	return true;
110 }
111 
client_update(rfbClient * client,int x,int y,int w,int h)112 static void client_update(rfbClient* client, int x, int y, int w, int h)
113 {
114 	if (!vncctx.dirty || x < vncctx.shmcont.dirty.x1)
115 		vncctx.shmcont.dirty.x1 = x;
116 
117 	if (!vncctx.dirty || y < vncctx.shmcont.dirty.y1)
118 		vncctx.shmcont.dirty.y1 = y;
119 
120 	if (!vncctx.dirty || (x+w) > vncctx.shmcont.dirty.x2)
121 		vncctx.shmcont.dirty.x2 = x + w;
122 
123 	if (!vncctx.dirty || (y+h) > vncctx.shmcont.dirty.y2)
124 		vncctx.shmcont.dirty.y2 = y + h;
125 
126 	vncctx.dirty = true;
127 }
128 
client_chat(rfbClient * client,int value,char * msg)129 static void client_chat(rfbClient* client, int value, char* msg)
130 {
131 /*
132  * send as message;
133  * we have rfbTextChatOpen, rfbTextChatClose, rfbTextChatFinished
134  */
135 	if (msg)
136 		LOG("msg (%s)\n", msg);
137 }
138 
client_selection(rfbClient * client,const char * text,int len)139 static void client_selection(rfbClient* client, const char* text, int len)
140 {
141 /* selection message */
142 	LOG("selection (%s)\n", text);
143 }
144 
client_keyled(rfbClient * client,int state,int mode)145 static void client_keyled(rfbClient* client, int state, int mode)
146 {
147 	LOG("ledstate (%d, %d)\n", state, mode);
148 }
149 
client_password(rfbClient * client)150 static char* client_password(rfbClient* client)
151 {
152 	LOG("(vnc-cl) requested password\n");
153 	return strdup(vncctx.pass);
154 }
155 
client_connect(const char * host,int port)156 static bool client_connect(const char* host, int port)
157 {
158 /* how to transfer / set credentials .. */
159 
160 	LOG("(vnc-cl) connecting to %s:%d\n", host, port);
161 	vncctx.client = rfbGetClient(8, 3, 4);
162 	vncctx.client->MallocFrameBuffer = client_resize;
163 	vncctx.client->canHandleNewFBSize = true;
164 	vncctx.client->GotFrameBufferUpdate = client_update;
165 	vncctx.client->HandleTextChat = client_chat;
166 	vncctx.client->GotXCutText = client_selection;
167 	vncctx.client->GetPassword = client_password;
168 	vncctx.client->HandleKeyboardLedState = client_keyled;
169 	vncctx.client->serverHost = strdup(host);
170 	vncctx.client->serverPort = port;
171 
172 /*
173  Note, if these are set, Rfb will actually BOUNCE
174 	vncctx.client->destHost = strdup(host);
175 	vncctx.client->destPort = port;
176  */
177 
178 	if (!rfbInitClient(vncctx.client, NULL, NULL)){
179 		LOG("(vnc-cl) couldn't initialize client.\n");
180 		return false;
181 	}
182 
183 	LOG("(vnc-cl) connected.\n");
184 	return true;
185 }
186 
cleanup()187 static void cleanup()
188 {
189 	if (vncctx.client)
190 		rfbClientCleanup(vncctx.client);
191 }
192 
cl_unstick()193 static void cl_unstick()
194 {
195 	SendKeyEvent(vncctx.client, XK_Shift_R, false);
196 	SendKeyEvent(vncctx.client, XK_Shift_L, false);
197 	SendKeyEvent(vncctx.client, XK_Control_R, false);
198 	SendKeyEvent(vncctx.client, XK_Control_L, false);
199 	SendKeyEvent(vncctx.client, XK_Alt_R, false);
200 	SendKeyEvent(vncctx.client, XK_Alt_L, false);
201 	SendKeyEvent(vncctx.client, XK_Meta_R, false);
202 	SendKeyEvent(vncctx.client, XK_Meta_L, false);
203 	SendKeyEvent(vncctx.client, XK_Super_L, false);
204 	SendKeyEvent(vncctx.client, XK_Super_R, false);
205 }
206 
map_cl_input(arcan_ioevent * ioev)207 static void map_cl_input(arcan_ioevent* ioev)
208 {
209 	static int mcount, mx, my, bmask;
210 	int sym = ioev->input.translated.keysym;
211 
212 	if (ioev->devkind != EVENT_IDEVKIND_KEYBOARD &&
213 		ioev->devkind != EVENT_IDEVKIND_MOUSE)
214 			return;
215 
216 	if (ioev->datatype == EVENT_IDATATYPE_TRANSLATED){
217 		int kv = 0;
218 
219 		if (sym >= 0 && sym < sizeof(symtbl_out) / sizeof(symtbl_out[0])){
220 			kv = symtbl_out[sym];
221 		}
222 
223 /* last resort, just use the unicode value and hope for the best */
224 		if (kv == 0)
225 			kv = ioev->subid;
226 
227 		SendKeyEvent(vncctx.client, kv, ioev->input.translated.active);
228 	}
229 /* just use one of the first four buttons of any device, not necessarily
230  * a mouse */
231 	else if (ioev->datatype == EVENT_IDATATYPE_DIGITAL){
232 		int btn = ioev->subid-1;
233 		int value = 0;
234 
235 		if (btn >= 0 && btn <= 4)
236 			value = mouse_button_map[btn];
237 
238 		bmask = ioev->input.digital.active ? bmask | value : bmask & ~value;
239 		SendPointerEvent(vncctx.client, mx, my, bmask);
240 	}
241 	else if (ioev->datatype == EVENT_IDATATYPE_ANALOG &&
242 		ioev->devkind == EVENT_IDEVKIND_MOUSE){
243 		if (ioev->subid == 0)
244 			mx = ioev->input.analog.axisval[0];
245 		else if (ioev->subid == 1)
246 			my = ioev->input.analog.axisval[0];
247 
248 /* only send when we get updates in pairs */
249 		if (++mcount % 2){
250 			mcount = 0;
251 			SendPointerEvent(vncctx.client, mx, my, bmask);
252 		}
253 	}
254 
255 	SendIncrementalFramebufferUpdateRequest(vncctx.client);
256 }
257 
dump_help()258 static void dump_help()
259 {
260 	fprintf(stdout, "Environment variables: \nARCAN_CONNPATH=path_to_server\n"
261 	  "ARCAN_ARG=packed_args (key1=value:key2:key3=value)\n\n"
262 		"Accepted packed_args:\n"
263 		"   key   \t   value   \t   description\n"
264 		"---------\t-----------\t-----------------\n"
265 		" pass    \t val       \t use this (7-bit ascii) password for auth\n"
266 	  " host    \t hostname  \t connect to the specified host\n"
267 		" port    \t portnum   \t use the specified port for connecting\n"
268 		" forcealpha           \t set alpha channel to fullbright (normally off)\n"
269 		"---------\t-----------\t----------------\n"
270 	);
271 }
272 
process_shmif()273 bool process_shmif()
274 {
275 	arcan_event inev;
276 
277 	while (arcan_shmif_poll(&vncctx.shmcont, &inev) > 0){
278 		if (inev.category == EVENT_TARGET)
279 			switch(inev.tgt.kind){
280 			case TARGET_COMMAND_STEPFRAME:
281 				SendFramebufferUpdateRequest(vncctx.client, 0, 0,
282 					vncctx.shmcont.addr->w, vncctx.shmcont.addr->h, FALSE);
283 			break;
284 
285 			case TARGET_COMMAND_EXIT:
286 				return false;
287 
288 			case TARGET_COMMAND_RESET:
289 				cl_unstick();
290 			break;
291 
292 			case TARGET_COMMAND_DISPLAYHINT:
293 /* not really useful here, there's a VNC extension to communicate display
294  * dimensions but afaik. not yet supported in libvncclient interface */
295 			break;
296 
297 			default:
298 				LOG("unhandled target event (%d:%s)\n",
299 					inev.tgt.kind, arcan_shmif_eventstr(&inev, NULL, 0));
300 			}
301 			else if (inev.category == EVENT_IO)
302 				map_cl_input(&inev.io);
303 			else
304 				LOG("unhandled event (%d:%s)\n", inev.tgt.kind,
305 					arcan_shmif_eventstr(&inev, NULL, 0));
306 		}
307 	return true;
308 }
309 
run_vnc(struct arcan_shmif_cont * con,struct arg_arr * args)310 int run_vnc(struct arcan_shmif_cont* con, struct arg_arr* args)
311 {
312 	const char* host;
313 
314 	if (!arg_lookup(args, "host", 0, &host)){
315 		arcan_shmif_last_words(con, "missing host argument");
316 		fprintf(stderr, "missing host argument\n");
317 		dump_help();
318 		return EXIT_FAILURE;
319 	}
320 
321 	arg_lookup(args, "pass", 0, (const char**) &vncctx.pass);
322 	if (!vncctx.pass)
323 		vncctx.pass = "";
324 
325 	gen_symtbl();
326 
327 /* client connect / loop */
328 	int port = 5900;
329 	const char* argtmp = NULL;
330 	if (arg_lookup(args, "port", 0, &argtmp)){
331 		port = strtoul(argtmp, NULL, 10);
332 	}
333 
334 	if (arg_lookup(args, "noalpha", 0, &argtmp))
335 		vncctx.forcealpha = true;
336 
337 	vncctx.shmcont = *con;
338 
339 	if (!client_connect(host, port))
340 		return EXIT_FAILURE;
341 
342 	arcan_event ev_cfail = {
343 		.category = EVENT_EXTERNAL,
344 		.ext.kind = ARCAN_EVENT(FAILURE),
345 		.ext.message = "(01) server connection broken"
346 	};
347 
348 /* this is cheating a bit as we don't guarantee the color format used */
349 	vncctx.client->frameBuffer = (uint8_t*) vncctx.shmcont.vidp;
350 	atexit( cleanup );
351 
352 	short poller = POLLHUP | POLLNVAL;
353 	short pollev = POLLIN | poller;
354 
355 	while (true){
356 		if (vncctx.dirty){
357 			vncctx.dirty = false;
358 			arcan_shmif_signal(&vncctx.shmcont, SHMIF_SIGVID);
359 			vncctx.shmcont.dirty.x1 = 0;
360 			vncctx.shmcont.dirty.y1 = 0;
361 			vncctx.shmcont.dirty.x2 = vncctx.shmcont.w;
362 			vncctx.shmcont.dirty.y2 = vncctx.shmcont.h;
363 		}
364 
365 		struct pollfd fds[2] = {
366 			{	.fd = vncctx.client->sock, .events = pollev},
367 			{ .fd = vncctx.shmcont.epipe, .events = pollev}
368 		};
369 
370 		int sv = poll(fds, 2, -1);
371 		if (0 == sv)
372 			continue;
373 
374 		if (-1 == sv){
375 			if (errno == EINTR || errno == EAGAIN)
376 				continue;
377 			else{
378 				LOG("polling on arcan signalling socket / RFB connection failed\n");
379 				break;
380 			}
381 		}
382 
383 		if ( ((fds[0].revents & POLLIN) && !HandleRFBServerMessage(vncctx.client))
384 			|| (fds[0].revents & poller)){
385 			arcan_shmif_enqueue(&vncctx.shmcont, &ev_cfail);
386 			arcan_shmif_drop(&vncctx.shmcont);
387 			break;
388 		}
389 
390 		int rev = fds[1].revents;
391 		if ( ((rev & POLLIN)) ){
392 			if (!process_shmif()){
393 				LOG("arcan- connection requested termination.\n");
394 				break;
395 			}
396 			if ((rev & poller)){
397 				uint8_t buf;
398 				if (-1 == read(fds[1].fd, &buf, 1)){
399 					LOG("arcan- connection socket failure: %s\n", strerror(errno));
400 				}
401 				else
402 					LOG("arcan connection suspiciously failed poll"
403 						" hup: %s, err: %s, nval: %s\n", (rev & POLLERR) ? "yes" : "no",
404 						(rev & POLLHUP) ? "yes" : "no", (rev & POLLNVAL) ? "yes" : "no");
405 				break;
406 			}
407 		}
408 	}
409 
410 	return EXIT_SUCCESS;
411 }
412