reflex-terminal 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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.doc/ext/reflex-terminal/native.cpp +19 -0
  3. data/.doc/ext/reflex-terminal/renderer.cpp +110 -0
  4. data/.doc/ext/reflex-terminal/terminal.cpp +530 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +12 -0
  6. data/.github/workflows/release-gem.yml +60 -0
  7. data/.github/workflows/tag.yml +35 -0
  8. data/.github/workflows/test.yml +46 -0
  9. data/.github/workflows/utils.rb +127 -0
  10. data/CONTRIBUTING.md +7 -0
  11. data/ChangeLog.md +6 -0
  12. data/Gemfile +5 -0
  13. data/LICENSE +21 -0
  14. data/README.md +100 -0
  15. data/Rakefile +44 -0
  16. data/VERSION +1 -0
  17. data/examples/terminal.rb +101 -0
  18. data/ext/reflex-terminal/defs.h +17 -0
  19. data/ext/reflex-terminal/extconf.rb +29 -0
  20. data/ext/reflex-terminal/native.cpp +19 -0
  21. data/ext/reflex-terminal/renderer.cpp +118 -0
  22. data/ext/reflex-terminal/terminal.cpp +572 -0
  23. data/include/reflex/terminal.h +233 -0
  24. data/include/reflex-terminal/defs.h +36 -0
  25. data/include/reflex-terminal/renderer.h +57 -0
  26. data/include/reflex-terminal/ruby/renderer.h +41 -0
  27. data/include/reflex-terminal/ruby/terminal.h +43 -0
  28. data/include/reflex-terminal/ruby.h +11 -0
  29. data/include/reflex-terminal.h +13 -0
  30. data/lib/reflex/bell_event.rb +15 -0
  31. data/lib/reflex/terminal.rb +168 -0
  32. data/lib/reflex/terminal_view.rb +268 -0
  33. data/lib/reflex-terminal/ext.rb +2 -0
  34. data/lib/reflex-terminal/extension.rb +41 -0
  35. data/lib/reflex-terminal.rb +7 -0
  36. data/reflex-terminal.gemspec +39 -0
  37. data/src/pty.cpp +236 -0
  38. data/src/renderer.cpp +365 -0
  39. data/src/terminal.cpp +1547 -0
  40. data/src/terminal.h +66 -0
  41. data/src/win32/pty.cpp +387 -0
  42. data/test/helper.rb +11 -0
  43. data/test/test_renderer.rb +113 -0
  44. data/test/test_terminal.rb +683 -0
  45. data/test/test_terminal_view.rb +182 -0
  46. metadata +152 -0
@@ -0,0 +1,233 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_H__
4
+ #define __REFLEX_TERMINAL_H__
5
+
6
+
7
+ #include <vector>
8
+ #include <map>
9
+ #include <optional>
10
+ #include <xot/pimpl.h>
11
+ #include <xot/string.h>
12
+ #include <xot/util.h>
13
+ #include <reflex/defs.h>
14
+ #include <reflex/event.h>
15
+
16
+
17
+ namespace Reflex
18
+ {
19
+
20
+
21
+ class Terminal
22
+ {
23
+
24
+ public:
25
+
26
+ struct Span
27
+ {
28
+
29
+ enum Attribute
30
+ {
31
+
32
+ BOLD = Xot::bit(0),
33
+
34
+ ITALIC = Xot::bit(1),
35
+
36
+ FAINT = Xot::bit(2),
37
+
38
+ BLINK = Xot::bit(3),
39
+
40
+ INVISIBLE = Xot::bit(4),
41
+
42
+ STRIKETHROUGH = Xot::bit(5),
43
+
44
+ OVERLINE = Xot::bit(6),
45
+
46
+ INVERSE = Xot::bit(7),
47
+
48
+ SELECTED = Xot::bit(8),
49
+
50
+ UNDERLINE_SHIFT = 9,
51
+
52
+ UNDERLINE_MASK = Xot::bit(UNDERLINE_SHIFT, 0x7)
53
+
54
+ };// Attribute
55
+
56
+ enum {COLOR_NONE = -1};
57
+
58
+ int x, width;// in cells
59
+
60
+ String text;// UTF-8, wide-cell spacers excluded
61
+
62
+ uint cell_offset, cell_size;
63
+
64
+ int fg, bg;// 0xRRGGBB, or COLOR_NONE
65
+
66
+ uint attribs;
67
+
68
+ };// Span
69
+
70
+ typedef std::vector<Span> SpanList;
71
+
72
+ typedef std::vector<SpanList> RowList;
73
+
74
+ typedef std::map<String, std::optional<String>> EnvMap;
75
+
76
+ struct Cursor
77
+ {
78
+
79
+ enum Style
80
+ {
81
+
82
+ BAR = 0,
83
+
84
+ BLOCK,
85
+
86
+ UNDERLINE,
87
+
88
+ BLOCK_HOLLOW
89
+
90
+ };// Style
91
+
92
+ int x, y;
93
+
94
+ Style style;
95
+
96
+ bool visible;
97
+
98
+ };// Cursor
99
+
100
+ enum ColorIndex
101
+ {
102
+
103
+ COLOR_FOREGROUND = 1,
104
+
105
+ COLOR_BACKGROUND,
106
+
107
+ COLOR_CURSOR,
108
+
109
+ COLOR_PALETTE_FIRST,
110
+
111
+ COLOR_PALETTE_LAST = COLOR_PALETTE_FIRST + 255
112
+
113
+ };// ColorIndex
114
+
115
+ enum OptionAsAlt
116
+ {
117
+
118
+ OPTION_AS_ALT_OFF = 0,
119
+
120
+ OPTION_AS_ALT_ON,
121
+
122
+ OPTION_AS_ALT_LEFT,
123
+
124
+ OPTION_AS_ALT_RIGHT,
125
+
126
+ OPTION_AS_ALT_MAX
127
+
128
+ };// OptionAsAlt
129
+
130
+ Terminal ();
131
+
132
+ Terminal (
133
+ int columns, int rows,
134
+ size_t scrollback_bytes = 8 * 1024 * 1024);
135
+
136
+ ~Terminal ();
137
+
138
+ bool update ();
139
+
140
+ void reset ();
141
+
142
+ void resize (
143
+ int columns, int rows,
144
+ int cell_width, int cell_height,
145
+ int screen_width, int screen_height);
146
+
147
+ void feed (const char* bytes, size_t size);
148
+
149
+ String read_pending_input ();
150
+
151
+ void spawn (const StringList& args = {}, const EnvMap& envs = {});
152
+
153
+ void close ();
154
+
155
+ void write (const char* bytes, size_t size);
156
+
157
+ void write_key (const KeyEvent& event);
158
+
159
+ void write_pointer (const PointerEvent& event);
160
+
161
+ void write_wheel (const WheelEvent& event);
162
+
163
+ void paste (const char* text, size_t size);
164
+
165
+ bool is_alive () const;
166
+
167
+ bool is_mouse_tracking () const;
168
+
169
+ void select (int x1, int y1, int x2, int y2);
170
+
171
+ void select_rect (int x1, int y1, int x2, int y2);
172
+
173
+ void select_word (int x, int y);
174
+
175
+ void select_line (int y);
176
+
177
+ void deselect ();
178
+
179
+ bool has_selection () const;
180
+
181
+ String selected_text () const;
182
+
183
+ void scroll_to (int row);
184
+
185
+ void scroll_by (int rows);
186
+
187
+ int scroll () const;
188
+
189
+ void set_option_as_alt (OptionAsAlt state);
190
+
191
+ OptionAsAlt option_as_alt () const;
192
+
193
+ int columns () const;
194
+
195
+ int rows () const;
196
+
197
+ Cursor cursor () const;
198
+
199
+ void set_default_color (ColorIndex index, const Color& color);
200
+
201
+ void clear_default_color (ColorIndex index);
202
+
203
+ bool get_default_color (ColorIndex index, Color* color) const;
204
+
205
+ bool get_color (ColorIndex index, Color* color) const;
206
+
207
+ const char* title () const;
208
+
209
+ StringList lines () const;
210
+
211
+ int history_rows () const;
212
+
213
+ StringList get_history_lines (int offset, int size) const;
214
+
215
+ longlong bells () const;
216
+
217
+ const RowList& spans () const;
218
+
219
+ operator bool () const;
220
+
221
+ bool operator ! () const;
222
+
223
+ struct Data;
224
+
225
+ Xot::PSharedImpl<Data> self;
226
+
227
+ };// Terminal
228
+
229
+
230
+ }// Reflex
231
+
232
+
233
+ #endif//EOH
@@ -0,0 +1,36 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_DEFS_H__
4
+ #define __REFLEX_TERMINAL_DEFS_H__
5
+
6
+
7
+ #include <reflex/defs.h>
8
+
9
+
10
+ #if defined(WIN32) && defined(GCC) && defined(REFLEX_TERMINAL)
11
+ #define REFLEX_TERMINAL_EXPORT __declspec(dllexport)
12
+ #else
13
+ #define REFLEX_TERMINAL_EXPORT
14
+ #endif
15
+
16
+
17
+ namespace ReflexTerminal
18
+ {
19
+
20
+
21
+ using namespace Xot ::Types;
22
+
23
+ using namespace Rays ::Types;
24
+
25
+ using namespace Reflex::Types;
26
+
27
+
28
+ using Rays::String;
29
+
30
+ using Rays::StringList;
31
+
32
+
33
+ }// ReflexTerminal
34
+
35
+
36
+ #endif//EOH
@@ -0,0 +1,57 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_RENDERER_H__
4
+ #define __REFLEX_TERMINAL_RENDERER_H__
5
+
6
+
7
+ #include <xot/pimpl.h>
8
+ #include <rays/bounds.h>
9
+ #include <rays/color.h>
10
+ #include <rays/font.h>
11
+ #include <rays/painter.h>
12
+ #include <reflex-terminal/defs.h>
13
+ #include <reflex/terminal.h>
14
+
15
+
16
+ namespace ReflexTerminal
17
+ {
18
+
19
+
20
+ class Renderer
21
+ {
22
+
23
+ public:
24
+
25
+ Renderer ();
26
+
27
+ Renderer (const Font& font);
28
+
29
+ ~Renderer ();
30
+
31
+ void set_font (const Font& font);
32
+
33
+ const Font& font () const;
34
+
35
+ coord cell_width () const;
36
+
37
+ coord cell_height () const;
38
+
39
+ void bake_glyphs (const Reflex::Terminal& terminal);
40
+
41
+ size_t glyph_count () const;
42
+
43
+ void draw (
44
+ Painter* painter, const Reflex::Terminal& terminal,
45
+ const Bounds& bounds);
46
+
47
+ struct Data;
48
+
49
+ Xot::PSharedImpl<Data> self;
50
+
51
+ };// Renderer
52
+
53
+
54
+ }// ReflexTerminal
55
+
56
+
57
+ #endif//EOH
@@ -0,0 +1,41 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_RUBY_RENDERER_H__
4
+ #define __REFLEX_TERMINAL_RUBY_RENDERER_H__
5
+
6
+
7
+ #include <rucy/class.h>
8
+ #include <rucy/extension.h>
9
+ #include <reflex-terminal/defs.h>
10
+ #include <reflex-terminal/renderer.h>
11
+
12
+
13
+ RUCY_DECLARE_VALUE_FROM_TO(REFLEX_TERMINAL_EXPORT, ReflexTerminal::Renderer)
14
+
15
+
16
+ namespace ReflexTerminal
17
+ {
18
+
19
+
20
+ REFLEX_TERMINAL_EXPORT Rucy::Class renderer_class ();
21
+ // class ReflexTerminal::Renderer
22
+
23
+
24
+ }// ReflexTerminal
25
+
26
+
27
+ namespace Rucy
28
+ {
29
+
30
+
31
+ template <> inline Class
32
+ get_ruby_class<ReflexTerminal::Renderer> ()
33
+ {
34
+ return ReflexTerminal::renderer_class();
35
+ }
36
+
37
+
38
+ }// Rucy
39
+
40
+
41
+ #endif//EOH
@@ -0,0 +1,43 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_RUBY_TERMINAL_H__
4
+ #define __REFLEX_TERMINAL_RUBY_TERMINAL_H__
5
+
6
+
7
+ #include <rucy/class.h>
8
+ #include <rucy/extension.h>
9
+ #include <reflex-terminal/defs.h>
10
+ #include <reflex/terminal.h>
11
+
12
+
13
+ RUCY_DECLARE_VALUE_FROM_TO(REFLEX_TERMINAL_EXPORT, Reflex::Terminal)
14
+
15
+ RUCY_DECLARE_CONVERT_TO(REFLEX_TERMINAL_EXPORT, Reflex::Terminal::OptionAsAlt)
16
+
17
+
18
+ namespace Reflex
19
+ {
20
+
21
+
22
+ REFLEX_TERMINAL_EXPORT Rucy::Class terminal_class ();
23
+ // class Reflex::Terminal
24
+
25
+
26
+ }// Reflex
27
+
28
+
29
+ namespace Rucy
30
+ {
31
+
32
+
33
+ template <> inline Class
34
+ get_ruby_class<Reflex::Terminal> ()
35
+ {
36
+ return Reflex::terminal_class();
37
+ }
38
+
39
+
40
+ }// Rucy
41
+
42
+
43
+ #endif//EOH
@@ -0,0 +1,11 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_RUBY_ALL_H__
4
+ #define __REFLEX_TERMINAL_RUBY_ALL_H__
5
+
6
+
7
+ #include <reflex-terminal/ruby/terminal.h>
8
+ #include <reflex-terminal/ruby/renderer.h>
9
+
10
+
11
+ #endif//EOH
@@ -0,0 +1,13 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __REFLEX_TERMINAL_ALL_H__
4
+ #define __REFLEX_TERMINAL_ALL_H__
5
+
6
+
7
+ #include <reflex/terminal.h>
8
+
9
+ #include <reflex-terminal/defs.h>
10
+ #include <reflex-terminal/renderer.h>
11
+
12
+
13
+ #endif//EOH
@@ -0,0 +1,15 @@
1
+ module Reflex
2
+
3
+
4
+ class BellEvent
5
+
6
+ def initialize(count = 1)
7
+ @count = count
8
+ end
9
+
10
+ attr_reader :count
11
+
12
+ end# BellEvent
13
+
14
+
15
+ end# Reflex
@@ -0,0 +1,168 @@
1
+ require 'xot/util'
2
+ require 'xot/const_symbol_accessor'
3
+ require 'xot/universal_accessor'
4
+ require 'reflex-terminal/ext'
5
+
6
+
7
+ module Reflex
8
+
9
+
10
+ class Terminal
11
+
12
+ PALETTE_RANGE = (0..(COLOR_PALETTE_LAST - COLOR_PALETTE_FIRST)).freeze
13
+
14
+ HISTORY_CHUNK_SIZE = 500
15
+
16
+ def initialize(
17
+ columns = 80, rows = 24,
18
+ # a memory budget rather than a line count: how many lines fit
19
+ # depends on how wide the terminal is. 0 keeps no scrollback
20
+ scrollback_bytes: 8 * 1024 * 1024)
21
+
22
+ initialize! columns, rows, scrollback_bytes
23
+ end
24
+
25
+ def resize(
26
+ columns, rows,
27
+ cell_width: 8, cell_height: 16,
28
+ screen_width: 0, screen_height: 0)
29
+
30
+ resize! columns, rows, cell_width, cell_height, screen_width, screen_height
31
+ end
32
+
33
+ # Starts a child process on a new pseudo terminal.
34
+ #
35
+ # Follows the Kernel#spawn convention: a leading hash sets environment
36
+ # variables (a nil value removes one), a single string runs via the
37
+ # shell (so quoting and pipes work), and multiple arguments exec
38
+ # directly. Unlike Kernel#spawn, unsetenv_others is not supported.
39
+ #
40
+ # @return [self] self
41
+ #
42
+ def spawn(*args)
43
+ envs = args.first.is_a?(Hash) ? args.shift : {}
44
+ args = args.compact
45
+ args = shell_command(args.first) if args.size == 1
46
+ spawn! args, envs
47
+ end
48
+
49
+ # Yields each style span of the visible screen.
50
+ #
51
+ # @yield [x, y, width, text, fg, bg, flags] a run of cells sharing
52
+ # the same style; x, y and width are in cells, and fg/bg are
53
+ # 0xRRGGBB or nil for the terminal's default color
54
+ #
55
+ # @return [Enumerator] when no block is given
56
+ #
57
+ def each_span(&block)
58
+ return enum_for :each_span unless block
59
+ each_span!(&block)
60
+ end
61
+
62
+ # Yields each line of the visible screen.
63
+ #
64
+ # @yield [line] a row of the screen, without its trailing spaces and
65
+ # without a newline of its own
66
+ #
67
+ # @return [Enumerator] when no block is given
68
+ #
69
+ def each_line(&block)
70
+ return enum_for :each_line unless block
71
+ each_line!(&block)
72
+ end
73
+
74
+ # @return [Array<String>] the visible screen, one string per row
75
+ #
76
+ def lines()
77
+ each_line.to_a
78
+ end
79
+
80
+ # Yields each line of the scrollback, oldest first.
81
+ #
82
+ # The history is walked in chunks rather than handed over at once,
83
+ # since it can hold far more than fits in memory.
84
+ #
85
+ # @yield [line] a line of the history, without its trailing spaces
86
+ #
87
+ # @return [Enumerator] when no block is given
88
+ #
89
+ def each_history_line(&block)
90
+ return enum_for :each_history_line unless block
91
+ (0...history_rows).step HISTORY_CHUNK_SIZE do |row|
92
+ get_history_lines!(row, HISTORY_CHUNK_SIZE).each(&block)
93
+ end
94
+ self
95
+ end
96
+
97
+ const_symbol_accessor :option_as_alt, **{
98
+ off: OPTION_AS_ALT_OFF,
99
+ on: OPTION_AS_ALT_ON,
100
+ left: OPTION_AS_ALT_LEFT,
101
+ right: OPTION_AS_ALT_RIGHT
102
+ }
103
+
104
+ def default_foreground_color=(color)
105
+ set_or_clear_default_color COLOR_FOREGROUND, color
106
+ end
107
+
108
+ def default_background_color=(color)
109
+ set_or_clear_default_color COLOR_BACKGROUND, color
110
+ end
111
+
112
+ def default_cursor_color=(color)
113
+ set_or_clear_default_color COLOR_CURSOR, color
114
+ end
115
+
116
+ def set_default_palette(index, color)
117
+ Array(index).each do |i|
118
+ set_or_clear_default_color to_palette_index(i), color
119
+ end
120
+ end
121
+
122
+ def default_foreground_color() = get_default_color! COLOR_FOREGROUND
123
+
124
+ def default_background_color() = get_default_color! COLOR_BACKGROUND
125
+
126
+ def default_cursor_color() = get_default_color! COLOR_CURSOR
127
+
128
+ def default_palette(index) = get_default_color! to_palette_index(index)
129
+
130
+ def foreground_color() = get_color! COLOR_FOREGROUND
131
+
132
+ def background_color() = get_color! COLOR_BACKGROUND
133
+
134
+ def cursor_color() = get_color! COLOR_CURSOR
135
+
136
+ def palette(index) = get_color! to_palette_index(index)
137
+
138
+ universal_accessor :default_foreground_color, :default_background_color,
139
+ :default_cursor_color
140
+
141
+ private
142
+
143
+ def shell_command(command)
144
+ if Xot.win32?
145
+ [ENV['COMSPEC'] || 'cmd.exe', '/c', command]
146
+ else
147
+ ['/bin/sh', '-c', command]
148
+ end
149
+ end
150
+
151
+ def set_or_clear_default_color(index, color)
152
+ if color
153
+ set_default_color! index, color
154
+ else
155
+ clear_default_color! index
156
+ end
157
+ end
158
+
159
+ def to_palette_index(index)
160
+ raise IndexError, "palette index out of range: #{index}" unless
161
+ PALETTE_RANGE.include? index
162
+ COLOR_PALETTE_FIRST + index
163
+ end
164
+
165
+ end# Terminal
166
+
167
+
168
+ end# Reflex