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