1 /*
2  * Copyright 2016-2018, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: https://arcan-fe.com
5  * Description: Quick and dirty FreeBSD specific event input layer. It seems
6  * like they will be moving, at least partially, to an evdev implementation so
7  * this is kept as minimal effort.
8  */
9 #include <stdlib.h>
10 #include <limits.h>
11 #include <stdint.h>
12 #include <stdbool.h>
13 #include <assert.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <termios.h>
17 #include <poll.h>
18 
19 #include <sys/consio.h>
20 #include <sys/mouse.h>
21 #include <sys/kbio.h>
22 #include <sys/select.h>
23 #include <sys/time.h>
24 #include <sys/types.h>
25 
26 #include <termios.h>
27 #include <ctype.h>
28 #include <signal.h>
29 #include <errno.h>
30 
31 #include "arcan_shmif.h"
32 #include "arcan_math.h"
33 #include "arcan_general.h"
34 #include "arcan_event.h"
35 
36 enum devtype {
37 /* use the TTY from /dev/console, user-specific device-node on rescan
38  * like /dev/kbd*) or the kbdmux abstraction. We run this in K_CODE mode
39  * and load the keymaps ourselves since there seem to be no mechanism for
40  * getting both from the same device at the same time.
41  * Expected to emit:
42  * datatype: EVENT_IDATATYPE_TRANSLATED,
43  * kind: EVENT_IDEVKIND_KEYBOPARD,
44  * with the input.translated.active set on press/release
45  * devid set to some unique index per device
46  * scancode/subid to scancode
47  * keysym to an SDL1.2 compatible SYM (if available)
48  * and utf8 to a textual representation (if available)
49  */
50 	keyboard = 0,
51 
52 /*
53  * expected to emit:
54  *  label : MOUSE,
55  *  kind  : EVENT_IO_AXIS_MOVE, EVENT_IO_BUTTON,
56  *  datatype : EVENT_IDATATYPE_ANALOG, EVENT_IDATATYPE_DIGITAL
57  *  with the reserved devid(-1) for mouse, subid for axis or button index.
58  * Analog values should ideally respect lower_bound, upper_bound, deadzone,
59  * kernel_size and mode (see sdl platform) and allow rebase ("warp") absolute
60  * sample values.
61  */
62 	mouse = 1,
63 	gamedev = 2,
64 	touchdev = 3,
65 	toggle = 4
66 };
67 
68 struct bsdkey {
69 	uint8_t u8[5];
70 	uint16_t sym;
71 	uint16_t modmask;
72 };
73 
74 struct devnode {
75 	enum devtype type;
76 	int fd;
77 	const char* nodepath;
78 
79 	union {
80 		struct {
81 /* go from scancode to */
82 			struct bsdkey lut[8][128];
83 			uint16_t mods;
84 		} keyb;
85 		struct {
86 			int base[2];
87 			int buttons;
88 			int mx, my;
89 			mousemode_t mode;
90 			mousehw_t hw;
91 		} mouse;
92 	};
93 };
94 
95 static struct {
96 	struct termios ttystate;
97 	struct devnode keyb, mdev;
98 	int tty;
99 	int sigpipe[2];
100 } evctx = {
101 	.tty = -1,
102 	.sigpipe = {-1, -1}
103 };
104 
105 static char* accents[] = {
106 	"dgra", "dacu", "dcir", "dtil", "dmac", "dbre", "ddot", "duml",
107 	"ddia", "dsla", "drin", "dced", "dapo", "ddac", "dogo", "dcar",
108 	NULL
109 };
110 
111 static const int vt_trm = 'z';
sigusr_term(int sign,siginfo_t * info,void * ctx)112 static void sigusr_term(int sign, siginfo_t* info, void* ctx)
113 {
114 	int s_errn = errno;
115 	if (write(evctx.sigpipe[1], &vt_trm, 1))
116 		;
117 	errno = s_errn;
118 }
119 
next_tok(char * str,char ** tok,size_t * cofs,size_t * out)120 static bool next_tok(char* str, char** tok, size_t* cofs, size_t* out)
121 {
122 /* scan for beginning */
123 	while(str[*cofs] && isspace(str[*cofs]))
124 		(*cofs)++;
125 
126 	if (!str[*cofs] || str[*cofs] == '#')
127 		return false;
128 	*tok = &str[*cofs];
129 
130 /* scan for end */
131 	if (str[*cofs] == '\''){
132 		(*cofs)++;
133 		while (str[*cofs] && str[*cofs] != '\'')
134 			(*cofs)++;
135 		(*cofs)++;
136 	}
137 	else {
138 		while (str[*cofs] && !isspace(str[*cofs]))
139 			(*cofs)++;
140 	}
141 
142 	*out = (uintptr_t)&str[*cofs] - (uintptr_t)*tok;
143 	return true;
144 }
145 
146 struct bsdk_ent {
147 	uint8_t key;
148 	uint16_t sym;
149 	uint16_t mask;
150 	char tok[8];
151 };
152 
153 static struct bsdk_ent bsdk_lut[] = {
154 	{.tok = "nop"},
155 	{.key =  0, .tok = "nul"}, {.key =  1, .tok = "soh"}, {.key =  2, .tok = "stx"},
156 	{.key =  3, .tok = "etx"}, {.key =  4, .tok = "eot"}, {.key =  5, .tok = "enq"},
157 	{.key =  6, .tok = "ack"}, {.key =  7, .tok = "bel"}, {.key =  8, .tok =  "bs", .sym = 8},
158 	{.key =  9, .tok = "ht", .sym = 9}, {.key = 10, .tok =  "lf"}, {.key = 11, .tok =  "vt"},
159 	{.key = 12, .tok =  "ff"}, {.key = 13, .tok =  "cr"}, {.key = 14, .tok =  "so"},
160 	{.key = 15, .tok =  "si"},
161 	{.key = 16, .tok = "dle"}, {.key = 17, .tok = "dc1"},
162 	{.key = 18, .tok = "dc2"}, {.key = 19, .tok = "dc3"}, {.key = 20, .tok = "dc4"},
163 	{.key = 21, .tok = "nak"}, {.key = 22, .tok = "syn"}, {.key = 23, .tok = "etb"},
164 	{.key = 24, .tok = "can"}, {.key = 25, .tok =  "em"}, {.key = 26, .tok = "sub"},
165 	{.key = 27, .tok = "esc"}, {.key = 28, .tok =  "fs"}, {.key = 29, .tok =  "gs"},
166 	{.key = 30, .tok =  "rs"}, {.key = 31, .tok =  "us"},
167 	{.tok = "lshift", .mask = ARKMOD_LSHIFT, .sym = 304},
168 	{.tok = "rshift", .mask = ARKMOD_RSHIFT, .sym = 303},
169 	{.tok = "lctrl",  .mask = ARKMOD_LCTRL,  .sym = 306},
170 	{.tok = "rctrl",  .mask = ARKMOD_RCTRL,  .sym = 305},
171 	{.tok = "lalt",   .mask = ARKMOD_LALT,   .sym = 308},
172 	{.tok = "ralt",   .mask = ARKMOD_RALT,   .sym = 307},
173 	{.tok = "clock",  .mask = ARKMOD_CAPS,   .sym = 301},
174 	{.tok = "nlock",  .mask = ARKMOD_NUM,    .sym = 300},
175 	{.tok = "meta",   .mask = ARKMOD_LMETA,  .sym = 309},
176 	{.tok = "fkey64", .mask = ARKMOD_RMETA,  .sym = 319},
177 	{.tok = "slock",  .sym = 302}, {.tok = "fkey01", .sym = 282},
178 	{.tok = "fkey02", .sym = 283}, {.tok = "fkey03", .sym = 284},
179 	{.tok = "fkey04", .sym = 285}, {.tok = "fkey05", .sym = 286},
180 	{.tok = "fkey06", .sym = 287}, {.tok = "fkey07", .sym = 288},
181 	{.tok = "fkey08", .sym = 289}, {.tok = "fkey09", .sym = 290},
182 	{.tok = "fkey10", .sym = 291}, {.tok = "fkey11", .sym = 292},
183 	{.tok = "fkey12", .sym = 293}, {.tok = "fkey49", .sym = 278},
184 	{.tok = "fkey50", .sym = 273}, {.tok = "fkey51", .sym = 280},
185 	{.tok = "fkey52", .sym = 269}, {.tok = "fkey53", .sym = 276},
186 	{.tok = "fkey54", .sym = 261}, {.tok = "fkey55", .sym = 275},
187 	{.tok = "fkey56", .sym = 270}, {.tok = "fkey57", .sym = 279},
188 	{.tok = "fkey58", .sym = 274}, {.tok = "fkey59", .sym = 281},
189 	{.tok = "fkey60", .sym = 277}, {.tok = "fkey61", .sym = 127},
190 	{.tok = "fkey62", .sym = 311}, {.tok = "fkey63", .sym = 312}
191 };
192 
to_utf8(uint16_t utf16,uint8_t out[4])193 static char* to_utf8(uint16_t utf16, uint8_t out[4])
194 {
195 	int count = 1, ofs = 0;
196 	uint32_t mask = 0x800;
197 
198 	if (utf16 >= 0x80)
199 		count++;
200 
201 	for(size_t i=0; i < 5; i++){
202 		if ( (uint32_t) utf16 >= mask )
203 			count++;
204 
205 		mask <<= 5;
206 	}
207 
208 	if (count == 1){
209 		out[0] = (char) utf16;
210 		out[1] = 0x00;
211 	}
212 	else {
213 		for (int i = (count-1 > 4 ? 4 : count - 1); i >= 0; i--){
214 			unsigned char ch = ( utf16 >> (6 * i)) & 0x3f;
215 			ch |= 0x80;
216 			if (i == count-1)
217 				ch |= 0xff << (8-count);
218 			out[ofs++] = ch;
219 		}
220 		out[ofs++] = 0x00;
221 	}
222 
223 	return (char*) out;
224 }
225 
decode_tok(char * tok)226 static struct bsdkey decode_tok(char* tok)
227 {
228 	struct bsdkey res = {.sym = 0};
229 
230 	if (tok[0] == '\''){
231 		res.u8[0] = tok[1];
232 		res.sym = tolower(res.u8[0]);
233 	}
234 /* the manpage says unicode or unicode as hex, but nothing about the
235  * actual encoding format, and some maps really give iso-8859-1 output
236  * but for some in vt/keymaps seem to be UCS2 */
237 	else if (isdigit(tok[0])){
238 		int ucs2 = strtoul(tok, NULL, tok[1] == 'x' ? 16 : 10);
239 		to_utf8(ucs2, res.u8);
240 	}
241 	else{
242 		for (size_t i = 0; i < sizeof(bsdk_lut) / sizeof(bsdk_lut[0]); i++)
243 			if (strcmp(tok, bsdk_lut[i].tok) == 0){
244 				res.u8[0] = bsdk_lut[i].key;
245 				res.modmask = bsdk_lut[i].mask;
246 				res.sym = bsdk_lut[i].sym ? bsdk_lut[i].sym : res.u8[0];
247 				break;
248 			}
249 	}
250 
251 	return res;
252 }
253 
platform_event_translation(int devid,int action,const char ** names,const char ** err)254 bool platform_event_translation(
255 	int devid, int action, const char** names, const char** err)
256 {
257 	*err = "Unsupported";
258 	return false;
259 }
260 
platform_event_device_request(int space,const char * path)261 int platform_event_device_request(int space, const char* path)
262 {
263 	return -1;
264 }
265 
load_keymap(struct devnode * dst,const char * path)266 static bool load_keymap(struct devnode* dst, const char* path)
267 {
268 	char line_in[128];
269 	FILE* fpek = fopen(path, "r");
270 	if (!fpek){
271 		arcan_warning("couldn't open keymap (%s)\n", path);
272 		return false;
273 	}
274 
275 	size_t linec = 0;
276 	while (!feof(fpek) && NULL != fgets(line_in, sizeof(line_in), fpek)){
277 		char* tok = NULL;
278 		size_t ofs = 0, nch;
279 
280 /* we're expecting 10 tokens per well-formed line:
281  * code base shft cntrl cntrl/shft alt alt/shft alt/cntrl alt/cntrl/shft lock
282  * num  chtok ...                                                        CH
283  * then there may be an empty row then the more complex format:
284  * grp ch/num ( chnum chnum ) that may also contain linefeeds
285 where the firstg
286  * is our keycode and the last is the lock-state modifier. The rest
287  * follow modifier patterns */
288 		size_t tokofs = 0, code = 0;
289 		while(next_tok(line_in, &tok, &ofs, &nch)){
290 			char old = tok[nch];
291 			tok[nch] = '\0';
292 			ofs++;
293 			if (tokofs){
294 				if (tokofs <= 8){
295 					dst->keyb.lut[tokofs-1][code] = decode_tok(tok);
296 				}
297 /* ignore the permanent group switch */
298 				else
299 					break;
300 				tokofs++;
301 			}
302 			else {
303 				if (isdigit(tok[0])){
304 					code = strtoul(tok, NULL, 10);
305 					tokofs++;
306 /* scancodes should be 7-bit with MSB being hold/released */
307 					if (code > 127)
308 						break;
309 				}
310 /* we are in accent, ignore for now */
311 				else
312 					break;
313 			}
314 		}
315 
316 		if (tok != NULL)
317 			linec++;
318 	}
319 	return true;
320 }
321 
platform_event_analogstate(int devid,int axisid,int * lower_bound,int * upper_bound,int * deadzone,int * kernel_size,enum ARCAN_ANALOGFILTER_KIND * mode)322 arcan_errc platform_event_analogstate(int devid, int axisid,
323 	int* lower_bound, int* upper_bound, int* deadzone,
324 	int* kernel_size, enum ARCAN_ANALOGFILTER_KIND* mode)
325 {
326 	return ARCAN_ERRC_NO_SUCH_OBJECT;
327 }
328 
platform_event_analogall(bool enable,bool mouse)329 void platform_event_analogall(bool enable, bool mouse)
330 {
331 }
332 
platform_event_analogfilter(int devid,int axisid,int lower_bound,int upper_bound,int deadzone,int buffer_sz,enum ARCAN_ANALOGFILTER_KIND kind)333 void platform_event_analogfilter(int devid,
334 	int axisid, int lower_bound, int upper_bound, int deadzone,
335 	int buffer_sz, enum ARCAN_ANALOGFILTER_KIND kind)
336 {
337 }
338 
mod_to_ind(uint16_t modmask)339 static int mod_to_ind(uint16_t modmask)
340 {
341 	int ind = 0;
342 
343 	if (modmask & (ARKMOD_LSHIFT | ARKMOD_RSHIFT)){
344 		ind += 1;
345 	}
346 
347 	if (modmask & (ARKMOD_LCTRL | ARKMOD_RCTRL)){
348 		ind += 2;
349 	}
350 
351 	if (modmask & (ARKMOD_LALT | ARKMOD_RALT)){
352 		ind += 4;
353 	}
354 
355 	return ind;
356 }
357 
do_touchp(arcan_evctx * ctx,struct devnode * node)358 static void do_touchp(arcan_evctx* ctx, struct devnode* node)
359 {
360 /* see man psm and the MOUSE_SYN_GETHWINFO etc. for synaptics */
361 }
362 
check_btn(arcan_evctx * ctx,int oldstate,int newstate,int fl,int ind)363 static inline void check_btn(arcan_evctx* ctx,
364 	int oldstate, int newstate, int fl, int ind)
365 {
366 	if (!((oldstate & fl) ^ (newstate & fl)))
367 		return;
368 
369 	arcan_event ev = {
370 	.category = EVENT_IO,
371 	.io = {
372 		.kind = EVENT_IO_BUTTON,
373 		.subid = MBTN_LEFT_IND + ind,
374 		.datatype = EVENT_IDATATYPE_DIGITAL,
375 			.devkind = EVENT_IDEVKIND_MOUSE,
376 			.input = { .digital = { .active = (oldstate & fl) } }
377 	}};
378 	arcan_event_enqueue(ctx, &ev);
379 }
380 
wheel_ev(arcan_evctx * ctx,int idofs,int val)381 static void wheel_ev(arcan_evctx* ctx, int idofs, int val)
382 {
383 	arcan_event aev = {
384 		.category = EVENT_IO,
385 		.io = {
386 			.kind = EVENT_IO_AXIS_MOVE,
387 			.datatype = EVENT_IDATATYPE_ANALOG,
388 			.devkind = EVENT_IDEVKIND_MOUSE,
389 			.subid = 2 + idofs,
390 			.input = {
391 				.analog = {
392 					.nvalues = 1,
393 					.gotrel = true,
394 					.axisval = {val}
395 				}
396 			},
397 		},
398 	};
399 	arcan_event_enqueue(ctx, &aev);
400 
401 	arcan_event dev = {
402 		.category = EVENT_IO,
403 			.io = {
404 				.kind = EVENT_IO_BUTTON,
405 /* sysmouse descriptions says add byte 6, 7 and treat as signed but
406  * from the mice I had to test with, down triggered 127 and up triggered 1 */
407 				.subid = MBTN_WHEEL_UP_IND + (val > 0 &&
408 					val <= 126 ? 1 : 0) + (idofs * 2),
409 				.datatype = EVENT_IDATATYPE_DIGITAL,
410 				.devkind = EVENT_IDEVKIND_MOUSE,
411 				.input = {
412 					.digital = {
413 						.active = true
414 					}
415 				}
416 			}
417 	};
418 	arcan_event_enqueue(ctx, &dev);
419 	dev.io.input.digital.active = false;
420 	arcan_event_enqueue(ctx, &dev);
421 }
422 
do_mouse(arcan_evctx * ctx,struct devnode * node)423 static void do_mouse(arcan_evctx* ctx, struct devnode* node)
424 {
425 	size_t pkt_sz = node->mouse.mode.packetsize;
426 	uint8_t buf[pkt_sz];
427 	arcan_event outev = {
428 		.category = EVENT_IO,
429 		.io = { .label = "mouse", .devkind = EVENT_IDEVKIND_MOUSE }
430 	};
431 
432 /* another option here would be to aggregate the motion events at least
433  * to lessen the impact of a pile-up casade from suspend or similar */
434 	while (read(node->fd, buf, pkt_sz) == pkt_sz){
435 		int buttons, dx, dy, wheel_h, wheel_v;
436 		buttons = dx = dy = wheel_h = wheel_v = 0;
437 
438 /* mouse.h seems to be the better source of documentation for deciphering */
439 		if (pkt_sz >= 5){
440 			buttons = (~buf[0] & 0x07);
441 			dx =  ((int8_t)buf[1] + (int8_t)buf[3]);
442 			dy = -((int8_t)buf[2] + (int8_t)buf[4]);
443 		}
444 
445 /* append the values to the button mask (but shifted, so do the same with
446  * the constant value for it to work. The reason is so that we can do a
447  * simple cmp on each mouse sample to know if we need to recheck individual
448  * buttons */
449 		if (pkt_sz >= 8){
450 			wheel_v = (int8_t)buf[5]+(int8_t)buf[6];
451 			buttons |= (buf[7] & MOUSE_SYS_EXTBUTTONS) << 3;
452 		}
453 /*
454  * 	seen this protocol somewhere but havn't anything to test with that
455  * 	actually activates it, so keep as a note for now
456 		if (pkt_sz >= 16){
457 			dx =  ((int16_t)((buf[ 8] << 9) | (buf[ 9] << 2)) >> 2);
458 			dy = -((int16_t)((buf[10] << 9) | (buf[11] << 2)) >> 2);
459 			wheel_v = -((int16_t)((buf[12] << 9) | (buf[13] << 2)) >> 2);
460 			wheel_h =  ((int16_t)((buf[14] << 9) | (buf[15] << 2)) >> 2);
461 		}
462  */
463 
464 /* our own input model has its problems too, absolute vs relative,
465  * separate events or grouped together, always %2 events or only on
466  * change, the interface is not particularly pretty */
467 		if (dx || dy){
468 			node->mouse.mx += dx;
469 			outev.io.datatype = EVENT_IDATATYPE_ANALOG;
470 			outev.io.kind = EVENT_IO_AXIS_MOVE;
471 			outev.io.input.analog.gotrel = true;
472 			outev.io.subid = 0;
473 			outev.io.input.analog.axisval[0] = node->mouse.mx;
474 			outev.io.input.analog.axisval[1] = dx;
475 			outev.io.input.analog.nvalues = 2;
476 			arcan_event_enqueue(ctx, &outev);
477 
478 			node->mouse.my += dy;
479 			outev.io.datatype = EVENT_IDATATYPE_ANALOG;
480 			outev.io.kind = EVENT_IO_AXIS_MOVE;
481 			outev.io.input.analog.gotrel = true;
482 			outev.io.subid = 1;
483 			outev.io.input.analog.axisval[0] = node->mouse.my;
484 			outev.io.input.analog.axisval[1] = dy;
485 			outev.io.input.analog.nvalues = 2;
486 			arcan_event_enqueue(ctx, &outev);
487 		}
488 
489 /* unfortunately we get a packed state table rather than changes-only,
490  * so we have to extract and check each one. sysmouse uses 1 to indicate
491  * released, we use that to indicate pressed */
492 		buttons = ~buttons;
493 
494 		if (buttons != node->mouse.buttons){
495 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON1UP, 0);
496 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON2UP, 1);
497 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON3UP, 2);
498 /* 3,4 and 5,6 are wheel digitals */
499 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON4UP << 3, 7);
500 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON5UP << 3, 8);
501 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON6UP << 3, 9);
502 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON7UP << 3,10);
503 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON8UP << 3,11);
504 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON9UP << 3,12);
505 			check_btn(ctx, node->mouse.buttons, buttons, MOUSE_SYS_BUTTON10UP <<3,13);
506 			node->mouse.buttons = buttons;
507 		}
508 
509 /* this has to be split into three events: [press+release] and analog axis */
510 		if (wheel_v)
511 			wheel_ev(ctx, 0, wheel_v);
512 
513 		if (wheel_h)
514 			wheel_ev(ctx, 1, wheel_h);
515  	}
516 }
517 
do_keyb(arcan_evctx * ctx,struct devnode * node)518 static void do_keyb(arcan_evctx* ctx, struct devnode* node)
519 {
520 	uint8_t n, code;
521 	ssize_t count = read(evctx.tty, &n, 1);
522 	if (count <= 0)
523 		return;
524 
525 	code = n & 0x7f;
526 	arcan_event ev = {
527 		.category = EVENT_IO,
528 		.io = {
529 			.kind = EVENT_IO_BUTTON,
530 			.devid = 1,
531 			.datatype = EVENT_IDATATYPE_TRANSLATED,
532 			.devkind = EVENT_IDEVKIND_KEYBOARD
533 		}
534 	};
535 
536 	ev.io.input.translated.scancode = code;
537 	ev.io.subid = code;
538 	ev.io.input.translated.active = (n & 0x80) == 0;
539 
540 /* it's safe to index based on modifier even though it's lagging behind
541  * here as all state fields repeat the same modifer */
542 	struct bsdkey* key = &evctx.keyb.keyb.lut[mod_to_ind(node->keyb.mods)][code];
543 	struct bsdkey* okey = &evctx.keyb.keyb.lut[0][code];
544 
545 /* the symbols are taken from their non-modified state */
546 
547 	memcpy(ev.io.input.translated.utf8, key->u8, 5);
548 	ev.io.input.translated.keysym = okey->sym;
549 	if ((n & 0x80) == 0)
550 		node->keyb.mods |= key->modmask;
551 	else
552 		node->keyb.mods &= ~(key->modmask);
553 
554 /* TODO: update code press bitmask table (2x 64-bit fields, 1 bit per code)
555  * and check for repeat, if repeat and not modifier then emit release+press */
556 	ev.io.input.translated.modifiers = node->keyb.mods;
557 	arcan_event_enqueue(ctx, &ev);
558 }
559 
platform_event_process(arcan_evctx * ctx)560 void platform_event_process(arcan_evctx* ctx)
561 {
562 /* KEYBOARD format:
563  * 1. Lookup code according to the current map which will yield
564  *    our basic entry.
565  * 2. If (modifier) update state tracking, translate modifier key
566  * 3. lookup entry in map using modifier index
567  * 4. use that to get SDL12 keysym, possible utf8- value
568  *    set scancode, subid (dup. code), modifier-states, sym, utf8
569  * 5. enqueue event.
570  */
571 	struct pollfd infd[3] = {
572 		{ .fd = evctx.tty, .events = POLLIN },
573 		{ .fd = evctx.mdev.fd, .events = POLLIN },
574 		{ .fd = evctx.sigpipe[0], .events = POLLIN }
575 	};
576 
577 /* allow one "sweep" */
578 	bool okst = true;
579 	while (okst && poll(infd, 2, 0) > 0){
580 		okst = false;
581 		if (infd[0].revents == POLLIN){
582 			do_keyb(ctx, &evctx.keyb);
583 			okst = true;
584 		};
585 
586 		if (infd[1].revents == POLLIN){
587 			do_mouse(ctx, &evctx.mdev);
588 			okst = true;
589 		}
590 
591 		if (infd[2].revents == POLLIN){
592 			char ch;
593 			if (1 == read(infd[2].fd, &ch, 1))
594 				switch(ch){
595 				case 'z':
596 					arcan_event_enqueue(arcan_event_defaultctx(), &(struct arcan_event){
597 						.category = EVENT_SYSTEM,
598 						.sys.kind = EVENT_SYSTEM_EXIT,
599 						.sys.errcode = EXIT_SUCCESS
600 					});
601 				break;
602 				}
603 		}
604 	}
605 }
606 
platform_event_samplebase(int devid,float xyz[3])607 void platform_event_samplebase(int devid, float xyz[3])
608 {
609 /* for mouse dev, run ioctl on the console with struct mouse_info,
610  * int_operttion, union { struct data, mode, event } */
611 }
612 
platform_event_keyrepeat(arcan_evctx * ctx,int * period,int * delay)613 void platform_event_keyrepeat(arcan_evctx* ctx, int* period, int* delay)
614 {
615 	struct keyboard_repeat rep;
616 	if (-1 == ioctl(evctx.keyb.fd, KDGETREPEAT, &rep))
617 		return;
618 
619 	if (*period < 0)
620 		*period = rep.kb_repeat[1];
621 	else
622 		rep.kb_repeat[1] = *period;
623 
624 	if (*delay < 0)
625 		*delay = rep.kb_repeat[0];
626 	else
627 		rep.kb_repeat[0] = *delay;
628 
629 	ioctl(evctx.keyb.fd, KDSETREPEAT, &rep);
630 }
631 
platform_event_capabilities(const char ** out)632 enum PLATFORM_EVENT_CAPABILITIES platform_event_capabilities(const char** out)
633 {
634 	if (out)
635 		*out = "freebsd";
636 
637 	return ACAP_TRANSLATED | ACAP_MOUSE | ACAP_TOUCH;
638 }
639 
platform_event_rescan_idev(arcan_evctx * ctx)640 void platform_event_rescan_idev(arcan_evctx* ctx)
641 {
642 }
643 
platform_event_devlabel(int devid)644 const char* platform_event_devlabel(int devid)
645 {
646 	return NULL;
647 }
648 
649 static char* envopts[] = {
650 	"ARCAN_INPUT_IGNORETTY", "Don't change terminal processing state",
651 	"ARCAN_INPUT_KEYMAPS", "(temporary), path to keymap to use",
652 	NULL
653 };
654 
platform_event_envopts()655 const char** platform_event_envopts()
656 {
657 	return (const char**) envopts;
658 }
659 
platform_event_deinit(arcan_evctx * ctx)660 void platform_event_deinit(arcan_evctx* ctx)
661 {
662 /* this is also performed in psep_open */
663 	if (-1 != evctx.tty){
664 		tcsetattr(evctx.tty, TCSAFLUSH, &evctx.ttystate);
665 		ioctl(evctx.tty, KDSETMODE, KD_TEXT);
666 		ioctl(evctx.keyb.fd, KDSKBMODE, K_XLATE);
667 	}
668 }
669 
platform_device_lock(int devind,bool state)670 void platform_device_lock(int devind, bool state)
671 {
672 }
673 
platform_event_preinit()674 void platform_event_preinit()
675 {
676 }
677 
platform_event_init(arcan_evctx * ctx)678 void platform_event_init(arcan_evctx* ctx)
679 {
680 /* save TTY settings, explicit for devices as we want kbd- access even
681  * when testing from a remote shell */
682 	evctx.keyb.fd = STDIN_FILENO;
683 
684 /* 7-bit scancode, 7 bit + MSB as active.  problem is that we need to do the
685  * translation ourselves because the driver can't output both scancodes and
686  * translated values, which would break cases where we need to forward scancode,
687  * like in VMs
688  */
689 	if (getenv("ARCAN_INPUT_KEYMAPS")){
690 		load_keymap(&evctx.keyb, getenv("ARCAN_INPUT_KEYMAPS"));
691 	}
692 /* just pick a layout, chances are the user can't / won't see the error so
693  * better to do something. Other (real) option should be to check rc.conf or
694  * rc.conf.local after the keymap variable and load that */
695 	else{
696 		arcan_warning("platform/freebsd: no keymap defined! set "
697 			"ARCAN_INPUT_KEYMAPS=/usr/share/syscons/keymaps/???.kbd");
698 		load_keymap(&evctx.keyb, "/usr/share/syscons/keymaps/us.iso.kbd");
699 	}
700 
701 	if (getenv("ARCAN_INPUT_IGNORETTY")){
702 		evctx.tty = -1;
703 		return;
704 	}
705 
706 	evctx.tty = STDIN_FILENO;
707 	tcgetattr(evctx.tty, &evctx.ttystate);
708 	ioctl(evctx.tty, KDSETMODE, KD_GRAPHICS);
709 
710 	if (-1 == ioctl(evctx.keyb.fd, KDSKBMODE, K_RAW)){
711 		arcan_warning("couldn't set code input mode\n");
712 	}
713 	struct termios raw = evctx.ttystate;
714 	cfmakeraw(&raw);
715 	tcsetattr(evctx.tty, TCSAFLUSH, &raw);
716 
717 	evctx.mdev.fd = platform_device_open("/dev/sysmouse", O_RDWR);
718 	if (-1 != evctx.mdev.fd){
719 		int level = 1;
720 		if (-1 == ioctl(evctx.mdev.fd, MOUSE_SETLEVEL, &level)){
721 			close(evctx.mdev.fd);
722 			evctx.mdev.fd = -1;
723 			arcan_warning("no acceptable mouse protocol found for sysmouse %d, %s\n",
724 				errno, strerror(errno));
725 			goto sigset;
726 		}
727 		if (-1 == ioctl(evctx.mdev.fd, MOUSE_GETMODE, &evctx.mdev.mouse.mode)){
728 			close(evctx.mdev.fd);
729 			arcan_warning("couldn't get mousemode from /dev/sysmouse\n");
730 			evctx.mdev.fd = -1;
731 			goto sigset;
732 		}
733 		if (evctx.mdev.mouse.mode.protocol != MOUSE_PROTO_SYSMOUSE ||
734 			evctx.mdev.mouse.mode.packetsize < 0){
735 			close(evctx.mdev.fd);
736 			evctx.mdev.fd = -1;
737 			arcan_warning("unexpected mouse protocol state\n");
738 			goto sigset;
739 		}
740 		int flags = fcntl(evctx.mdev.fd, F_GETFL);
741 		if (-1 == fcntl(evctx.mdev.fd, F_SETFL, flags | O_NONBLOCK)){
742 			close(evctx.mdev.fd);
743 			evctx.mdev.fd = -1;
744 			arcan_warning("couldn't set non-blocking mouse device\n");
745 			goto sigset;
746 		}
747 	}
748 
749 sigset:
750 /* use a pipe to handle TERM / VT switching */
751 	if (0 != pipe(evctx.sigpipe)){
752 		arcan_fatal("couldn't create signalling pipe, code: %d, reason: %s\n",
753 			errno, strerror(errno));
754 
755 		fcntl(evctx.sigpipe[0], F_SETFD, FD_CLOEXEC);
756 		fcntl(evctx.sigpipe[1], F_SETFD, FD_CLOEXEC);
757 		struct sigaction er_sh = {.sa_handler = SIG_IGN};
758 		sigaction(SIGINT, &er_sh, NULL);
759 		er_sh.sa_handler = NULL;
760 		er_sh.sa_sigaction = sigusr_term;
761 		er_sh.sa_flags = SA_RESTART;
762 		sigaction(SIGTERM, &er_sh, NULL);
763 	}
764 }
765 
platform_event_reset(arcan_evctx * ctx)766 void platform_event_reset(arcan_evctx* ctx)
767 {
768 	platform_event_deinit(ctx);
769 	platform_event_init(ctx);
770 }
771