2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
27 const ev_tag_key : int := 1;
28 const ev_tag_mouse_move : int := 2;
29 const ev_tag_mouse_button : int := 3;
30 const ev_tag_redraw : int := 4;
31 const ev_tag_resize : int := 5;
33 record event_keyboard [
48 x y prev_buttons buttons double_buttons wx wy : int;
53 keyboard : event_keyboard;
54 resize : event_resize;
55 redraw : event_redraw;
60 // the f* keys must be contiguous
61 const key_f1 : char := -1;
62 const key_f2 : char := -2;
63 const key_f3 : char := -3;
64 const key_f4 : char := -4;
65 const key_f5 : char := -5;
66 const key_f6 : char := -6;
67 const key_f7 : char := -7;
68 const key_f8 : char := -8;
69 const key_f9 : char := -9;
70 const key_f10 : char := -10;
71 const key_f11 : char := -11;
72 const key_f12 : char := -12;
73 const key_left : char := -13;
74 const key_right : char := -14;
75 const key_up : char := -15;
76 const key_down : char := -16;
77 const key_insert : char := -17;
78 const key_delete : char := -18;
79 const key_home : char := -19;
80 const key_end : char := -20;
81 const key_page_up : char := -21;
82 const key_page_down : char := -22;
83 const key_backspace : char := -23;
84 const key_enter : char := -24;
85 const key_num_5 : char := -25;
86 const key_tab : char := -26;
87 const key_esc : char := -27;
89 const key_flag_shift : int := 1;
90 const key_flag_ctrl : int := 2;
91 const key_flag_alt : int := 4;
93 const key_to_termcap : list(int) := [
122 fn event_get_keyboard~spark(w : world, h : handle, tc : termcap, loc : locale, q : msgqueue(event)) : bottom_type;
123 fn event_get_resize~spark(w : world, h : handle, q : msgqueue(event)) : bottom_type;
124 fn event_get_mouse~spark(w : world, h : handle, q : msgqueue(event)) : bottom_type;
125 fn event_wait_for_any_key(w : world, h : handle) : world;
126 fn event_reset_handle(w : world, h : handle) : world;
134 const terminal_read_timeout : int64 := 100000;
136 fn os2_vkmap : list(char)
138 var a := fill(char, 0, #100);
140 a[14] := key_backspace;
155 a[73] := key_page_up;
161 a[81] := key_page_down;
197 a[118] := key_page_down;
199 a[132] := key_page_up;
211 a[146] := key_insert;
212 a[147] := key_delete;
215 a[153] := key_page_up;
220 a[161] := key_page_down;
221 a[162] := key_insert;
222 a[163] := key_delete;
226 fn os2_ctrlmap(p : list(int32)) : int
230 if p3 bt 0 or p3 bt 1 then
231 result or= key_flag_shift;
233 result or= key_flag_ctrl;
235 result or= key_flag_alt;
239 fn dos_vkmap : list(char)
297 fn dos_ctrlmap(p : list(int32)) : int
300 if p[2] = #00 or p[2] = #e0 then [
303 p1 >= #54 and p1 <= #5d or
304 p1 >= #87 and p1 <= #88 then
305 result or= key_flag_shift;
306 if p1 >= #5e and p1 <= #67 or
307 p1 >= #73 and p1 <= #78 or
309 p1 >= #89 and p1 <= #8a or
310 p1 >= #8e and p1 <= #94 then
311 result or= key_flag_ctrl;
313 p1 >= #10 and p1 <= #1c or
314 p1 >= #1e and p1 <= #29 or
315 p1 >= #2b and p1 <= #35 or
320 p1 >= #68 and p1 <= #71 or
321 p1 >= #78 and p1 <= #83 or
322 p1 >= #8b and p1 <= #8c or
323 p1 >= #97 and p1 <= #99 or
326 p1 >= #9f and p1 <= #a3 or
327 p1 >= #a5 and p1 <= #a6 then
328 result or= key_flag_alt;
333 fn win32_vkmap : list(char)
335 var a := fill(char, 0, #80);
336 a[#08] := key_backspace;
341 a[#21] := key_page_up;
342 a[#22] := key_page_down;
349 a[#2d] := key_insert;
350 a[#2e] := key_delete;
366 fn win32_ctrlmap(p : list(int32)) : int
370 if p3 bt 0 or p3 bt 1 then
371 result or= key_flag_alt;
372 if p3 bt 2 or p3 bt 3 then
373 result or= key_flag_ctrl;
375 result or= key_flag_shift;
379 type mouse_context := list(int64);
381 fn new_mouse_context := infinite_uninitialized(int64);
383 fn send_mouse_event(implicit w : world, implicit mouse : mouse_context, q : msgqueue(event), ev : event_mouse) : (world, mouse_context)
385 var xtag, xev := msgqueue_receive_tag_nonblock(q, ev_tag_mouse_move);
386 if not is_exception xtag then [
387 ev.wx += xev.mouse.wx;
388 ev.wy += xev.mouse.wy;
390 ev.double_buttons := 0;
391 var button_down_mask := ev.buttons and not ev.prev_buttons;
392 if button_down_mask <> 0 then [
393 var time := get_monotonic_time();
394 while button_down_mask <> 0 do [
395 var b := bsr button_down_mask;
396 button_down_mask btr= b;
397 if not is_uninitialized(mouse[b]), time - mouse[b] <= 300000 then
398 ev.double_buttons bts= b;
402 msgqueue_send(q, select(ev.prev_buttons = ev.buttons, ev_tag_mouse_button, ev_tag_mouse_move), event.mouse.(ev));
409 key_map : treemap(bytes, event_keyboard);
410 partial_map : treeset(bytes);
412 last_mouse_buttons : int;
413 mouse : mouse_context;
416 fn kbd_is_partial_locale_character(ctx : kbd_context, b : bytes) : bool
418 return not locale_validate(ctx.loc, b);
421 fn kbd_test_input(ctx : kbd_context, b : bytes) : int
425 var test := treeset_test(ctx.partial_map, b[ .. i]) or kbd_is_partial_locale_character(ctx, b[ .. i]);
427 var timer := sleep~lazy(unit_value, terminal_read_timeout);
428 if any(test, timer) then [
429 //eval debug("timeout " + ntos(i));
442 fn event_get_xterm_mouse(implicit w : world, ctx : kbd_context, mb mx my mm : int) : (world, kbd_context)
444 var xm := event_mouse.[
453 //eval debug("x: " + ntos(mx) + ", y: " + ntos(my) + ", b: " + ntos(mb));
456 if mb = 0 then buttons := 1;
457 if mb = 1 then buttons := 4;
458 if mb = 2 then buttons := 2;
459 if mb = 64 then xm.wy := -1;
460 if mb = 65 then xm.wy := 1;
461 if mb = 66 then xm.wx := -1;
462 if mb = 67 then xm.wx := 1;
463 if mb = 128 then buttons := 8;
464 if mb = 129 then buttons := 16;
465 if mb = 130 then buttons := 32;
467 if xm.wx <> 0 and mm = 0 then
471 xm.buttons := buttons;
473 xm.buttons := ctx.last_mouse_buttons and not buttons;
475 xm.buttons := ctx.last_mouse_buttons or buttons;
477 xm.prev_buttons := ctx.last_mouse_buttons;
479 ctx.last_mouse_buttons := xm.buttons;
481 ctx.mouse := send_mouse_event(ctx.mouse, ctx.q, xm);
486 fn event_get_keyboard_loop(implicit w : world, ctx : kbd_context, b : bytes) : bottom_type
489 if not len_greater_than(b, 0) then
491 var i := kbd_test_input(ctx, b);
493 if ctx.has_mouse, i >= 3 then [
494 var xm : list(event);
495 var timer := sleep~lazy(unit_value, terminal_read_timeout);
496 if b[ .. 3] = bytes.[ 27, 91, 77 ] then [
497 var mb := b[3] - ' ';
498 var mx := b[4] - ' ';
499 var my := b[5] - ' ';
500 if any(my, timer) then [
504 ctx := event_get_xterm_mouse(ctx, mb, mx, my, -1);
505 //return xm + event_get_keyboard_lazy(ctx, b[6 .. ]);
509 if b[ .. 3] = bytes.[ 27, 91, 60 ] then [
510 fn mM(b : byte) : bool := b = 'm' or b = 'M';
511 var end := list_search_fn(b, mM);
512 if any(end, timer) then [
516 //eval debug(b[3 .. end + 1]);
517 var mbs := list_break(b[3 .. end], ';');
518 var mb := ston(mbs[0]);
519 var mx := ston(mbs[1]);
520 var my := ston(mbs[2]);
521 if is_exception mb or is_exception mx or is_exception my then [
525 ctx := event_get_xterm_mouse(ctx, mb, mx, my, select(b[end] = 'M', 0, 1));
526 //return xm + event_get_keyboard_lazy(ctx, b[end + 1 .. ]);
531 var xk := treemap_search(ctx.key_map, b[ .. i]);
533 //return [ event.keyboard.(xk.j) ] + event_get_keyboard_lazy(ctx, b[i .. ]);
534 msgqueue_send(ctx.q, ev_tag_key, event.keyboard.(xk.j));
538 var start := select(b[0] = 27, 0, 1);
539 if start = 1, i = 2 then [
540 xk := treemap_search(ctx.key_map, b[start .. i]);
542 if xk.j.key <> key_esc then
543 xk.j.flags or= key_flag_alt;
544 //return [ event.keyboard.(xk.j) ] + event_get_keyboard_lazy(ctx, b[i .. ]);
545 msgqueue_send(ctx.q, ev_tag_key, event.keyboard.(xk.j));
550 if locale_validate(ctx.loc, b[start .. i]) then [
551 var uc := locale_to_string(ctx.loc, b[start .. i]);
552 if len_at_least(uc, 1) and classify_character(uc[0]) >= class_one then [
553 xk := maybe(event_keyboard).j.(event_keyboard.[ key : uc[0], flags : 0, rep : 1 ]);
555 xk.j.flags or= key_flag_alt;
556 msgqueue_send(ctx.q, ev_tag_key, event.keyboard.(xk.j));
571 fn kbd_add_mapping(ctx : kbd_context, str : bytes, ev : event_keyboard) : kbd_context
573 if treemap_test(ctx.key_map, str) then
575 for i := 0 to len(str) do
576 ctx.partial_map := treeset_set(ctx.partial_map, str[ .. i]);
577 if not is_uninitialized(ev) then
578 ctx.key_map := treemap_insert(ctx.key_map, str, ev);
582 fn do_packet_console(implicit w : world, h : handle, q : msgqueue(event), os : int, vkmap : list(char), ctrlmap : fn (list(int32)) : int) : bottom_type
584 var mouse := new_mouse_context;
586 var cached_locale := exception_make(locale, ec_sync, error_invalid_operation, 0, false);
588 var packet := read_console_packet(h);
589 //xeval debug("got packet: " + ntos(packet[0]) + ", " + ntos(packet[1]) + ", " + ntos(packet[2]) + ", " + ntos(packet[3]) + ", " + ntos(packet[4]));
590 if packet[0] = 1 then [
592 var flags : int := ctrlmap(packet);
593 if os = SystemProperty_OS_DOS or os = SystemProperty_OS_OS2 then [
594 if packet[1] = 224 and packet[2] = 13 then [
598 if packet[2] >= 32 and packet[2] <> 224 then [
602 if packet[1] >= 0, packet[1] < len(vkmap), vkmap[packet[1]] <> 0 then [
603 key := vkmap[packet[1]];
604 if key > 0 and flags = 0 then
611 if key >= 1 and key < 32 then [
613 flags or= key_flag_ctrl;
615 flags and= not key_flag_shift;
616 if key >= #7f, packet[4] <> 0 then [
617 if packet[4] <> cached_cp then [
618 cached_cp := packet[4];
619 cached_locale := locale_get(".cp" + ntos(packet[4]));
620 //eval debug("cached locale: " + select(is_exception(cached_locale), "ex", "op"));
622 if is_exception cached_locale then
624 var str := locale_to_string(cached_locale, bytes.[ key ]);
625 if len(str) <> 1 then
630 //eval debug("key=" + ntos(key) + ",flags=" + ntos(flags));
632 var ev := event.keyboard.(event_keyboard.[ key : key, flags : flags, rep : 1 ] );
633 msgqueue_send(q, ev_tag_key, ev);
634 ] else if packet[0] = 2 then [
635 var ev := event_mouse.[ x : packet[1], y : packet[2], prev_buttons : packet[3], buttons : packet[4], wx : packet[5], wy : packet[6], soft_cursor : select(packet[7] <> 0, false, true) ];
636 mouse := send_mouse_event(mouse, q, ev);
643 fn event_get_keyboard_internal(implicit w : world, h : handle, tc : termcap, loc : locale, q : msgqueue(event)) : bottom_type
645 var os := sysprop(SystemProperty_OS);
646 if os = SystemProperty_OS_DOS then [
647 var vkmap := dos_vkmap;
648 var ctrlmap := dos_ctrlmap;
649 return do_packet_console(h, q, os, vkmap, ctrlmap);
651 if os = SystemProperty_OS_OS2 then [
652 var vkmap := os2_vkmap;
653 var ctrlmap := os2_ctrlmap;
654 return do_packet_console(h, q, os, vkmap, ctrlmap);
656 if os = SystemProperty_OS_Windows then [
657 var vkmap := win32_vkmap;
658 var ctrlmap := win32_ctrlmap;
659 return do_packet_console(h, q, os, vkmap, ctrlmap);
661 stty(h, stty_flag_raw or stty_flag_noecho);
662 var ctx := kbd_context.[
666 key_map : treemap_init(bytes, event_keyboard),
667 partial_map : treeset_init(bytes),
668 has_mouse : termcap_has_mouse(tc),
669 last_mouse_buttons : 0,
670 mouse : new_mouse_context,
672 for i := 1 to len(key_to_termcap) do [
673 var str := termcap_query_string(tc, key_to_termcap[i]);
676 ctx := kbd_add_mapping(ctx, str, event_keyboard.[
682 for i := tc_s_key_f13 to tc_s_key_f49 do [
684 var str := termcap_query_string(tc, i);
687 if i < tc_s_key_f25 then
688 flags := key_flag_shift;
689 else if i < tc_s_key_f37 then
690 flags := key_flag_ctrl;
691 else if i < tc_s_key_f49 then
692 flags := key_flag_shift or key_flag_ctrl;
695 ctx := kbd_add_mapping(ctx, str, event_keyboard.[
696 key : key_f1 - (i - tc_s_key_f13) mod 12,
701 ctx := kbd_add_mapping(ctx, bytes.[ 0 ], event_keyboard.[ key : ' ', flags : key_flag_ctrl, rep : 1 ]);
702 ctx := kbd_add_mapping(ctx, bytes.[ 9 ], event_keyboard.[ key : key_tab, flags : 0, rep : 1 ]);
703 ctx := kbd_add_mapping(ctx, bytes.[ 13 ], event_keyboard.[ key : key_enter, flags : 0, rep : 1 ]);
704 ctx := kbd_add_mapping(ctx, bytes.[ 27 ], event_keyboard.[ key : key_esc, flags : 0, rep : 1 ]);
705 for i := 1 to 32 do [
706 ctx := kbd_add_mapping(ctx, bytes.[ i ], event_keyboard.[
708 flags : key_flag_ctrl,
712 if ctx.has_mouse then [
713 ctx := kbd_add_mapping(ctx, bytes.[ 27, 91, 77 ], uninitialized(event_keyboard));
714 ctx := kbd_add_mapping(ctx, bytes.[ 27, 91, 60 ], uninitialized(event_keyboard));
716 var b := read_lazy(h);
717 return event_get_keyboard_loop(ctx, b);
720 fn event_get_keyboard~spark(implicit w : world, h : handle, tc : termcap, loc : locale, q : msgqueue(event)) : bottom_type
722 var err := event_get_keyboard_internal(h, tc, loc, q);
724 var err_event := exception_copy(event, err);
725 msgqueue_send(q, ev_tag_key, err_event);
730 fn event_get_resize~spark(implicit w : world, h : handle, q : msgqueue(event)) : bottom_type
734 x, y := tty_size(h, x, y);
735 var ev : event := event.resize.(event_resize.[ x : x, y : y ]);
738 xtag, xev := msgqueue_receive_tag_nonblock(q, ev_tag_redraw);
739 xtag, xev := msgqueue_receive_tag_nonblock(q, ev_tag_resize);
740 msgqueue_send(q, ev_tag_resize, ev);
747 fn event_get_mouse~spark(implicit w : world, h : handle, q : msgqueue(event)) : bottom_type
749 var mouse := new_mouse_context;
751 var s := socket(pf_unix, sock_stream, 0);
752 connect(s, bytes.[ pf_unix, 0 ] + "/dev/gpmctl");
756 //eval debug("connected");
758 var procname := readlink(dnone(), "/proc/self");
759 var ttylink := readlink(dnone(), "/proc/self/fd/1");
763 var cdata := empty(byte);
764 cdata += int_to_native(native.short, not 0);
765 cdata += int_to_native(native.short, 0);
766 cdata += int_to_native(native.short, 0);
767 cdata += int_to_native(native.short, 0);
768 cdata += int_to_native(native.integer, ston(procname));
770 if list_begins_with(ttylink, "/dev/tty") then
771 vc := ston(ttylink[8 .. ]);
772 else if list_begins_with(ttylink, "/dev/vc/") then
773 vc := ston(ttylink[8 .. ]);
776 cdata += int_to_native(native.integer, vc);
781 var b := read_lazy(s);
783 var button_state := 0;
787 var x := native_to_int(native.short, b[8 .. 10]) - 1;
788 var y := native_to_int(native.short, b[10 .. 12]) - 1;
789 var etype := native_to_int(native.integer, b[12 .. 16]);
792 if gpmb bt 0 then buttons bts= 1;
793 if gpmb bt 1 then buttons bts= 2;
794 if gpmb bt 2 then buttons bts= 0;
795 if gpmb bt 3 then buttons bts= 3;
796 if gpmb bt 4 then buttons bts= 4;
797 var new_button_state := button_state;
799 new_button_state or= buttons;
801 new_button_state and= not buttons;
802 var wx := -native_to_int(native.short, b[24 .. 26]);
803 var wy := -native_to_int(native.short, b[26 .. 28]);
804 var ev := event_mouse.[ x : x, y : y, prev_buttons : button_state, buttons : new_button_state, wx : wx, wy : wy, soft_cursor : true ];
806 mouse := send_mouse_event(mouse, q, ev);
810 button_state := new_button_state;
816 fn event_wait_for_any_key(implicit w : world, h : handle) : world
818 var os := sysprop(SystemProperty_OS);
819 if os = SystemProperty_OS_DOS or
820 os = SystemProperty_OS_OS2 or
821 os = SystemProperty_OS_Windows then [
823 var k := read_console_packet(h);
828 stty(h, stty_flag_raw or stty_flag_noecho);
829 var k := read_partial(h, 10);
831 event_reset_handle(h);
835 fn event_reset_handle(implicit w : world, h : handle) : world