rubyterm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,71 @@
1
+ # See:
2
+ # https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Controls-beginning-with-ESC
3
+ #
4
+ class EscapeParser
5
+ attr_reader :str, :state
6
+
7
+ def initialize
8
+ @state = :start
9
+ @str = ""
10
+ end
11
+
12
+ def put(ch)
13
+ # ch is a codepoint, which for OSC/DCS string payloads (e.g. a window
14
+ # title) can be a multibyte UTF-8 character - decode it as UTF-8.
15
+ # Bare Integer#chr only handles 0..255 and raises RangeError on
16
+ # anything larger (e.g. a spinner glyph in claude's title sequence).
17
+ c = ch.chr(Encoding::UTF_8)
18
+ case @state
19
+ when :start
20
+ return false if ch < 32
21
+ @str << c
22
+ case c
23
+ when '['
24
+ @state = :csi
25
+ when ']'
26
+ @state = :oc
27
+ when 'P', 'X', '^', '_'
28
+ # DCS, SOS, PM, APC - string sequences terminated by ST (ESC \) or BEL
29
+ @state = :string
30
+ when /\w|[=>|}~6-9]/
31
+ @state = :complete
32
+ end
33
+ when :csi
34
+ # A C0 control byte inside a CSI is executed immediately and the
35
+ # sequence keeps parsing (per ECMA-48) - it must NOT be folded into
36
+ # the sequence or terminate it. Returning false routes it to Term's
37
+ # handle_control while @esc stays in :csi; ESC (27) restarts the
38
+ # sequence there. (vttest "cursor-control chars inside ESC sequences".)
39
+ return false if ch < 32
40
+ @str << c
41
+ @state = :complete if /[[:alpha:]]|[@]/.match(c)
42
+ when :oc
43
+ if ch == 7 then @state = :complete
44
+ elsif ch < 32 then return false
45
+ else @str << c
46
+ end
47
+ when :string
48
+ if ch == 7
49
+ @state = :complete
50
+ elsif ch == 27
51
+ @state = :string_esc
52
+ elsif ch < 32
53
+ return false
54
+ else
55
+ @str << c
56
+ end
57
+ when :string_esc
58
+ if c == '\\'
59
+ @state = :complete
60
+ else
61
+ @str << 27.chr << c
62
+ @state = :string
63
+ end
64
+ else
65
+ return false
66
+ end
67
+ true
68
+ end
69
+
70
+ def complete?; @state == :complete; end
71
+ end
data/lib/keymap.rb ADDED
@@ -0,0 +1,112 @@
1
+
2
+
3
+ $kmap = nil
4
+ def update_keymap(dpy)
5
+ reply = dpy.get_keyboard_mapping
6
+
7
+
8
+ if reply
9
+ $kmap = reply.keysyms.map do |c|
10
+ if c == 0
11
+ nil
12
+ elsif X11::KeySyms[c]
13
+ X11::KeySyms[c]
14
+ elsif c < 0x100
15
+ c.chr(Encoding::ISO_8859_1)
16
+ elsif c.between?(0xffb0, 0xffb9)
17
+ "KP_#{c-0xffb0}".to_sym
18
+ elsif c.between?(0xffbe, 0xffe0)
19
+ "f#{c-0xffbe+1}".to_sym
20
+ elsif c.between?(0xff08, 0xffff)
21
+ # FIXME:
22
+ raise "keyboard_#{c.to_s(16)}".to_s
23
+ elsif c.between?(0x01000100, 0x0110FFFF)
24
+ (c-0x01000100).
25
+ chr(Encoding::UTF_32) rescue c.to_s(16)
26
+ else
27
+ STDERR.puts "keymap: unknown_#{c.to_s(16)}"
28
+ end
29
+ end.each_slice(reply.keysyms_per_keycode).to_a
30
+ #ks = ks.map {|s| s.compact.sort_by{|x| x.to_s}.uniq }.to_a # This is for testing/ease of reading only
31
+ end
32
+ end
33
+
34
+ def lookup_keysym(dpy, event)
35
+ update_keymap(dpy) if !$kmap
36
+ $kmap[event.detail-dpy.display_info.min_keycode] rescue nil
37
+ end
38
+
39
+ SPECIALS = {
40
+ "+" => :"ctrl_+",
41
+ "-" => :"ctrl_-"
42
+ }
43
+
44
+ def lookup_string(dpy, event)
45
+ ks = Array(lookup_keysym(dpy, event))
46
+ str = ""
47
+ shift = event.state.anybits?(0x01)
48
+ meta = event.state.anybits?(0x08)
49
+ ctrl = event.state.anybits?(0x04)
50
+ i = shift && ks[1] ? 1 : 0
51
+
52
+ return SPECIALS[ks[i]] if ctrl && SPECIALS[ks[i]]
53
+
54
+ if shift
55
+ case ks[i]
56
+ # FIXME: This is messed up.
57
+ when :XK_ISO_Left_Tab then return :shift_tab, nil
58
+ when :XK_Down; then return :shift_down, nil
59
+ when :XK_Up; then return :shift_up, nil
60
+ when :XK_Page_Up; then return :shift_page_up, nil
61
+ when :XK_Page_Down; then return :shift_page_down, nil
62
+ end
63
+ end
64
+
65
+ if ks[i].is_a?(String)
66
+ if ctrl
67
+ str = (ks[i][0].ord & 0x9f).chr # Strip 0x60
68
+ elsif meta
69
+ str = "\e#{ks[i]}"
70
+ else
71
+ str = ks[i]
72
+ end
73
+ end
74
+ return ks[i], str
75
+ end
76
+
77
+
78
+
79
+ # Map X key events to escapes
80
+ KEYMAP = {
81
+ :enter => "\r",
82
+ :backspace => "\x7F",
83
+ :tab => "\t",
84
+ :XK_Escape => "\e",
85
+ :f1 => "\e[11~",
86
+ :f2 => "\e[12~",
87
+ :f3 => "\e[13~",
88
+ :f4 => "\e[14~",
89
+ :f5 => "\e[15~",
90
+ :f6 => "\e[16~",
91
+ :f7 => "\e[17~",
92
+ :f8 => "\e[18~",
93
+ :f9 => "\e[19~",
94
+ :f10 => "\e[20~",
95
+ :f11 => "\e[21~",
96
+ :f12 => "\e[22~",
97
+ :shift_up => "\e[1;2A",
98
+ :shift_down => "\e[1;2B",
99
+ :shift_tab => "\e[Z",
100
+ :XK_Left => "\e[D",
101
+ :XK_Right => "\e[C",
102
+ :XK_Down => "\e[B",
103
+ :XK_ISO_Left_Tab => "\t",
104
+ :XK_Up => "\e[A",
105
+ :XK_Delete => "\e[P",
106
+ :XK_Page_Up => "\x1b[5~",
107
+ :XK_Page_Down => "\x1b[6~",
108
+ }
109
+
110
+ def keysym_to_vt102(ks)
111
+ KEYMAP[ks]
112
+ end
data/lib/palette.rb ADDED
@@ -0,0 +1,14 @@
1
+ PALETTE_BASIC = [
2
+ 0x000000, 0xd73737, 0x60ac39, 0xae9513, 0x6684e1, 0xb854d4, 0x1fad83, 0xcccccc,
3
+ 0x7d7a68, 0xff3737, 0x80dc39, 0xfff013, 0x6694ff, 0xd854ff, 0x1fdda3, 0xffffff]
4
+
5
+ PALETTE256= PALETTE_BASIC +
6
+ [0,0x5f, 0x87, 0xaf, 0xd7, 0xff].repeated_permutation(3).sort.map{|ary| (ary[0]<<16)+(ary[1]<<8)+ary[2]}+
7
+ (8..244).step(10).map{|n| (n<<16)+(n<<8)+n}
8
+
9
+ # Default foreground/background, stored as basic-palette indices (strings;
10
+ # Term resolves them to colours). Defined here so the library is
11
+ # self-contained; guarded so callers that pre-define them (tests, harness)
12
+ # don't clash.
13
+ FG = "7" unless defined?(FG)
14
+ BG = "0" unless defined?(BG)