reline 0.2.7 → 0.3.1
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.
- checksums.yaml +4 -4
- data/lib/reline/ansi.rb +64 -22
- data/lib/reline/config.rb +34 -3
- data/lib/reline/general_io.rb +3 -1
- data/lib/reline/key_actor/emacs.rb +1 -1
- data/lib/reline/key_stroke.rb +64 -14
- data/lib/reline/line_editor.rb +630 -72
- data/lib/reline/terminfo.rb +50 -5
- data/lib/reline/unicode.rb +42 -3
- data/lib/reline/version.rb +1 -1
- data/lib/reline/windows.rb +151 -49
- data/lib/reline.rb +127 -22
- metadata +3 -3
data/lib/reline/terminfo.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
|
-
|
2
|
-
require 'fiddle
|
1
|
+
begin
|
2
|
+
require 'fiddle'
|
3
|
+
require 'fiddle/import'
|
4
|
+
rescue LoadError
|
5
|
+
module Reline::Terminfo
|
6
|
+
def self.curses_dl
|
7
|
+
false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
3
11
|
|
4
12
|
module Reline::Terminfo
|
5
13
|
extend Fiddle::Importer
|
@@ -50,7 +58,7 @@ module Reline::Terminfo
|
|
50
58
|
@curses_dl = nil if @curses_dl == false
|
51
59
|
@curses_dl
|
52
60
|
end
|
53
|
-
end
|
61
|
+
end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl)
|
54
62
|
|
55
63
|
module Reline::Terminfo
|
56
64
|
dlload curses_dl
|
@@ -66,12 +74,27 @@ module Reline::Terminfo
|
|
66
74
|
#extern 'char *tparm(const char *str, ...)'
|
67
75
|
@tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP)
|
68
76
|
end
|
69
|
-
|
77
|
+
begin
|
78
|
+
#extern 'int tigetflag(char *str)'
|
79
|
+
@tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
80
|
+
rescue Fiddle::DLError
|
81
|
+
# OpenBSD lacks tigetflag
|
82
|
+
#extern 'int tgetflag(char *str)'
|
83
|
+
@tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
84
|
+
end
|
85
|
+
begin
|
86
|
+
#extern 'int tigetnum(char *str)'
|
87
|
+
@tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
88
|
+
rescue Fiddle::DLError
|
89
|
+
# OpenBSD lacks tigetnum
|
90
|
+
#extern 'int tgetnum(char *str)'
|
91
|
+
@tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
92
|
+
end
|
70
93
|
|
71
94
|
def self.setupterm(term, fildes)
|
72
95
|
errret_int = String.new("\x00" * 8, encoding: 'ASCII-8BIT')
|
73
96
|
ret = @setupterm.(term, fildes, errret_int)
|
74
|
-
errret = errret_int.
|
97
|
+
errret = errret_int.unpack1('i')
|
75
98
|
case ret
|
76
99
|
when 0 # OK
|
77
100
|
0
|
@@ -114,6 +137,28 @@ module Reline::Terminfo
|
|
114
137
|
@tiparm.(str, *new_args).to_s
|
115
138
|
end
|
116
139
|
|
140
|
+
def self.tigetflag(capname)
|
141
|
+
flag = @tigetflag.(capname).to_i
|
142
|
+
case flag
|
143
|
+
when -1
|
144
|
+
raise TerminfoError, "not boolean capability: #{capname}"
|
145
|
+
when 0
|
146
|
+
raise TerminfoError, "can't find capability: #{capname}"
|
147
|
+
end
|
148
|
+
flag
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.tigetnum(capname)
|
152
|
+
num = @tigetnum.(capname).to_i
|
153
|
+
case num
|
154
|
+
when -2
|
155
|
+
raise TerminfoError, "not numeric capability: #{capname}"
|
156
|
+
when -1
|
157
|
+
raise TerminfoError, "can't find capability: #{capname}"
|
158
|
+
end
|
159
|
+
num
|
160
|
+
end
|
161
|
+
|
117
162
|
def self.enabled?
|
118
163
|
true
|
119
164
|
end
|
data/lib/reline/unicode.rb
CHANGED
@@ -79,6 +79,8 @@ class Reline::Unicode
|
|
79
79
|
|
80
80
|
require 'reline/unicode/east_asian_width'
|
81
81
|
|
82
|
+
HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/
|
83
|
+
|
82
84
|
MBCharWidthRE = /
|
83
85
|
(?<width_2_1>
|
84
86
|
[#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...)
|
@@ -93,6 +95,12 @@ class Reline::Unicode
|
|
93
95
|
#{ EastAsianWidth::TYPE_H }
|
94
96
|
| #{ EastAsianWidth::TYPE_NA }
|
95
97
|
| #{ EastAsianWidth::TYPE_N }
|
98
|
+
)(?!#{ HalfwidthDakutenHandakuten })
|
99
|
+
| (?<width_2_3>
|
100
|
+
(?: #{ EastAsianWidth::TYPE_H }
|
101
|
+
| #{ EastAsianWidth::TYPE_NA }
|
102
|
+
| #{ EastAsianWidth::TYPE_N })
|
103
|
+
#{ HalfwidthDakutenHandakuten }
|
96
104
|
)
|
97
105
|
| (?<ambiguous_width>
|
98
106
|
#{EastAsianWidth::TYPE_A}
|
@@ -101,15 +109,15 @@ class Reline::Unicode
|
|
101
109
|
|
102
110
|
def self.get_mbchar_width(mbchar)
|
103
111
|
ord = mbchar.ord
|
104
|
-
if (0x00 <= ord and ord <= 0x1F)
|
112
|
+
if (0x00 <= ord and ord <= 0x1F) # in EscapedPairs
|
105
113
|
return 2
|
106
|
-
elsif (0x20 <= ord and ord <= 0x7E)
|
114
|
+
elsif (0x20 <= ord and ord <= 0x7E) # printable ASCII chars
|
107
115
|
return 1
|
108
116
|
end
|
109
117
|
m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE)
|
110
118
|
case
|
111
119
|
when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER
|
112
|
-
when m[:width_2_1], m[:width_2_2] then 2
|
120
|
+
when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2
|
113
121
|
when m[:width_3] then 3
|
114
122
|
when m[:width_0] then 0
|
115
123
|
when m[:width_1] then 1
|
@@ -185,6 +193,37 @@ class Reline::Unicode
|
|
185
193
|
[lines, height]
|
186
194
|
end
|
187
195
|
|
196
|
+
# Take a chunk of a String cut by width with escape sequences.
|
197
|
+
def self.take_range(str, start_col, max_width, encoding = str.encoding)
|
198
|
+
chunk = String.new(encoding: encoding)
|
199
|
+
total_width = 0
|
200
|
+
rest = str.encode(Encoding::UTF_8)
|
201
|
+
in_zero_width = false
|
202
|
+
rest.scan(WIDTH_SCANNER) do |gc|
|
203
|
+
case
|
204
|
+
when gc[NON_PRINTING_START_INDEX]
|
205
|
+
in_zero_width = true
|
206
|
+
when gc[NON_PRINTING_END_INDEX]
|
207
|
+
in_zero_width = false
|
208
|
+
when gc[CSI_REGEXP_INDEX]
|
209
|
+
chunk << gc[CSI_REGEXP_INDEX]
|
210
|
+
when gc[OSC_REGEXP_INDEX]
|
211
|
+
chunk << gc[OSC_REGEXP_INDEX]
|
212
|
+
when gc[GRAPHEME_CLUSTER_INDEX]
|
213
|
+
gc = gc[GRAPHEME_CLUSTER_INDEX]
|
214
|
+
if in_zero_width
|
215
|
+
chunk << gc
|
216
|
+
else
|
217
|
+
mbchar_width = get_mbchar_width(gc)
|
218
|
+
total_width += mbchar_width
|
219
|
+
break if (start_col + max_width) < total_width
|
220
|
+
chunk << gc if start_col < total_width
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
chunk
|
225
|
+
end
|
226
|
+
|
188
227
|
def self.get_next_mbchar_size(line, byte_pointer)
|
189
228
|
grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first
|
190
229
|
grapheme ? grapheme.bytesize : 0
|
data/lib/reline/version.rb
CHANGED
data/lib/reline/windows.rb
CHANGED
@@ -42,6 +42,14 @@ class Reline::Windows
|
|
42
42
|
}.each_pair do |key, func|
|
43
43
|
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
44
44
|
end
|
45
|
+
|
46
|
+
# Emulate ANSI key sequence.
|
47
|
+
{
|
48
|
+
[27, 91, 90] => :completion_journey_up, # S-Tab
|
49
|
+
}.each_pair do |key, func|
|
50
|
+
config.add_default_key_binding_by_keymap(:emacs, key, func)
|
51
|
+
config.add_default_key_binding_by_keymap(:vi_insert, key, func)
|
52
|
+
end
|
45
53
|
end
|
46
54
|
|
47
55
|
if defined? JRUBY_VERSION
|
@@ -106,6 +114,7 @@ class Reline::Windows
|
|
106
114
|
SCROLLLOCK_ON = 0x0040
|
107
115
|
SHIFT_PRESSED = 0x0010
|
108
116
|
|
117
|
+
VK_TAB = 0x09
|
109
118
|
VK_END = 0x23
|
110
119
|
VK_HOME = 0x24
|
111
120
|
VK_LEFT = 0x25
|
@@ -133,9 +142,11 @@ class Reline::Windows
|
|
133
142
|
@@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
|
134
143
|
@@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
|
135
144
|
@@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
|
145
|
+
@@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L')
|
136
146
|
|
137
147
|
@@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L')
|
138
148
|
@@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L')
|
149
|
+
@@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L')
|
139
150
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
|
140
151
|
|
141
152
|
private_class_method def self.getconsolemode
|
@@ -157,7 +168,9 @@ class Reline::Windows
|
|
157
168
|
@@input_buf = []
|
158
169
|
@@output_buf = []
|
159
170
|
|
160
|
-
|
171
|
+
@@output = STDOUT
|
172
|
+
|
173
|
+
def self.msys_tty?(io = @@hConsoleInputHandle)
|
161
174
|
# check if fd is a pipe
|
162
175
|
if @@GetFileType.call(io) != FILE_TYPE_PIPE
|
163
176
|
return false
|
@@ -173,7 +186,7 @@ class Reline::Windows
|
|
173
186
|
# DWORD FileNameLength;
|
174
187
|
# WCHAR FileName[1];
|
175
188
|
# } FILE_NAME_INFO
|
176
|
-
len = p_buffer[0, 4].
|
189
|
+
len = p_buffer[0, 4].unpack1("L")
|
177
190
|
name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
|
178
191
|
|
179
192
|
# Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX')
|
@@ -197,10 +210,34 @@ class Reline::Windows
|
|
197
210
|
[ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ],
|
198
211
|
[ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ],
|
199
212
|
[ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ],
|
213
|
+
|
214
|
+
# Emulate ANSI key sequence.
|
215
|
+
[ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ],
|
200
216
|
]
|
201
217
|
|
218
|
+
@@hsg = nil
|
219
|
+
|
202
220
|
def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
203
221
|
|
222
|
+
# high-surrogate
|
223
|
+
if 0xD800 <= char_code and char_code <= 0xDBFF
|
224
|
+
@@hsg = char_code
|
225
|
+
return
|
226
|
+
end
|
227
|
+
# low-surrogate
|
228
|
+
if 0xDC00 <= char_code and char_code <= 0xDFFF
|
229
|
+
if @@hsg
|
230
|
+
char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00
|
231
|
+
@@hsg = nil
|
232
|
+
else
|
233
|
+
# no high-surrogate. ignored.
|
234
|
+
return
|
235
|
+
end
|
236
|
+
else
|
237
|
+
# ignore high-surrogate without low-surrogate if there
|
238
|
+
@@hsg = nil
|
239
|
+
end
|
240
|
+
|
204
241
|
key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state)
|
205
242
|
|
206
243
|
match = KEY_MAP.find { |args,| key.matches?(**args) }
|
@@ -212,30 +249,42 @@ class Reline::Windows
|
|
212
249
|
# no char, only control keys
|
213
250
|
return if key.char_code == 0 and key.control_keys.any?
|
214
251
|
|
252
|
+
@@output_buf.push("\e".ord) if key.control_keys.include?(:ALT)
|
253
|
+
|
215
254
|
@@output_buf.concat(key.char.bytes)
|
216
255
|
end
|
217
256
|
|
218
257
|
def self.check_input_event
|
219
258
|
num_of_events = 0.chr * 8
|
220
|
-
while @@output_buf.empty?
|
221
|
-
|
222
|
-
|
259
|
+
while @@output_buf.empty?
|
260
|
+
Reline.core.line_editor.resize
|
261
|
+
if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec
|
262
|
+
# prevent for background consolemode change
|
263
|
+
@@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0)
|
264
|
+
next
|
265
|
+
end
|
266
|
+
next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0
|
267
|
+
input_records = 0.chr * 20 * 80
|
223
268
|
read_event = 0.chr * 4
|
224
|
-
if @@ReadConsoleInputW.(@@hConsoleInputHandle,
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
269
|
+
if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_records, 80, read_event) != 0
|
270
|
+
read_events = read_event.unpack1('L')
|
271
|
+
0.upto(read_events) do |idx|
|
272
|
+
input_record = input_records[idx * 20, 20]
|
273
|
+
event = input_record[0, 2].unpack1('s*')
|
274
|
+
case event
|
275
|
+
when WINDOW_BUFFER_SIZE_EVENT
|
276
|
+
@@winch_handler.()
|
277
|
+
when KEY_EVENT
|
278
|
+
key_down = input_record[4, 4].unpack1('l*')
|
279
|
+
repeat_count = input_record[8, 2].unpack1('s*')
|
280
|
+
virtual_key_code = input_record[10, 2].unpack1('s*')
|
281
|
+
virtual_scan_code = input_record[12, 2].unpack1('s*')
|
282
|
+
char_code = input_record[14, 2].unpack1('S*')
|
283
|
+
control_key_state = input_record[16, 2].unpack1('S*')
|
284
|
+
is_key_down = key_down.zero? ? false : true
|
285
|
+
if is_key_down
|
286
|
+
process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
|
287
|
+
end
|
239
288
|
end
|
240
289
|
end
|
241
290
|
end
|
@@ -256,7 +305,7 @@ class Reline::Windows
|
|
256
305
|
end
|
257
306
|
|
258
307
|
def self.empty_buffer?
|
259
|
-
if not @@
|
308
|
+
if not @@output_buf.empty?
|
260
309
|
false
|
261
310
|
elsif @@kbhit.call == 0
|
262
311
|
true
|
@@ -265,17 +314,37 @@ class Reline::Windows
|
|
265
314
|
end
|
266
315
|
end
|
267
316
|
|
268
|
-
def self.
|
317
|
+
def self.get_console_screen_buffer_info
|
318
|
+
# CONSOLE_SCREEN_BUFFER_INFO
|
319
|
+
# [ 0,2] dwSize.X
|
320
|
+
# [ 2,2] dwSize.Y
|
321
|
+
# [ 4,2] dwCursorPositions.X
|
322
|
+
# [ 6,2] dwCursorPositions.Y
|
323
|
+
# [ 8,2] wAttributes
|
324
|
+
# [10,2] srWindow.Left
|
325
|
+
# [12,2] srWindow.Top
|
326
|
+
# [14,2] srWindow.Right
|
327
|
+
# [16,2] srWindow.Bottom
|
328
|
+
# [18,2] dwMaximumWindowSize.X
|
329
|
+
# [20,2] dwMaximumWindowSize.Y
|
269
330
|
csbi = 0.chr * 22
|
270
|
-
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)
|
331
|
+
return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0
|
332
|
+
csbi
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.get_screen_size
|
336
|
+
unless csbi = get_console_screen_buffer_info
|
337
|
+
return [1, 1]
|
338
|
+
end
|
271
339
|
csbi[0, 4].unpack('SS').reverse
|
272
340
|
end
|
273
341
|
|
274
342
|
def self.cursor_pos
|
275
|
-
csbi =
|
276
|
-
|
277
|
-
|
278
|
-
|
343
|
+
unless csbi = get_console_screen_buffer_info
|
344
|
+
return Reline::CursorPos.new(0, 0)
|
345
|
+
end
|
346
|
+
x = csbi[4, 2].unpack1('s')
|
347
|
+
y = csbi[6, 2].unpack1('s')
|
279
348
|
Reline::CursorPos.new(x, y)
|
280
349
|
end
|
281
350
|
|
@@ -295,6 +364,7 @@ class Reline::Windows
|
|
295
364
|
|
296
365
|
def self.move_cursor_down(val)
|
297
366
|
if val > 0
|
367
|
+
return unless csbi = get_console_screen_buffer_info
|
298
368
|
screen_height = get_screen_size.first
|
299
369
|
y = cursor_pos.y + val
|
300
370
|
y = screen_height - 1 if y > (screen_height - 1)
|
@@ -305,42 +375,74 @@ class Reline::Windows
|
|
305
375
|
end
|
306
376
|
|
307
377
|
def self.erase_after_cursor
|
308
|
-
csbi =
|
309
|
-
|
310
|
-
cursor = csbi[4, 4].
|
378
|
+
return unless csbi = get_console_screen_buffer_info
|
379
|
+
attributes = csbi[8, 2].unpack1('S')
|
380
|
+
cursor = csbi[4, 4].unpack1('L')
|
311
381
|
written = 0.chr * 4
|
312
382
|
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written)
|
313
|
-
@@FillConsoleOutputAttribute.call(@@hConsoleHandle,
|
383
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written)
|
314
384
|
end
|
315
385
|
|
316
386
|
def self.scroll_down(val)
|
317
|
-
return if val
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
@@
|
387
|
+
return if val < 0
|
388
|
+
return unless csbi = get_console_screen_buffer_info
|
389
|
+
buffer_width, buffer_lines, x, y, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s')
|
390
|
+
screen_height = window_bottom - window_top + 1
|
391
|
+
val = screen_height if val > screen_height
|
392
|
+
|
393
|
+
if @@legacy_console || window_left != 0
|
394
|
+
# unless ENABLE_VIRTUAL_TERMINAL,
|
395
|
+
# if srWindow.Left != 0 then it's conhost.exe hosted console
|
396
|
+
# and puts "\n" causes horizontal scroll. its glitch.
|
397
|
+
# FYI irb write from culumn 1, so this gives no gain.
|
398
|
+
scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4')
|
399
|
+
destination_origin = 0 # y * 65536 + x
|
400
|
+
fill = [' '.ord, attributes].pack('SS')
|
401
|
+
@@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill)
|
402
|
+
else
|
403
|
+
origin_x = x + 1
|
404
|
+
origin_y = y - window_top + 1
|
405
|
+
@@output.write [
|
406
|
+
(origin_y != screen_height) ? "\e[#{screen_height};H" : nil,
|
407
|
+
"\n" * val,
|
408
|
+
(origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil
|
409
|
+
].join
|
410
|
+
end
|
324
411
|
end
|
325
412
|
|
326
413
|
def self.clear_screen
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
414
|
+
if @@legacy_console
|
415
|
+
return unless csbi = get_console_screen_buffer_info
|
416
|
+
buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s')
|
417
|
+
fill_length = buffer_width * (window_bottom - window_top + 1)
|
418
|
+
screen_topleft = window_top * 65536
|
419
|
+
written = 0.chr * 4
|
420
|
+
@@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written)
|
421
|
+
@@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written)
|
422
|
+
@@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft)
|
423
|
+
else
|
424
|
+
@@output.write "\e[2J" "\e[H"
|
425
|
+
end
|
338
426
|
end
|
339
427
|
|
340
428
|
def self.set_screen_size(rows, columns)
|
341
429
|
raise NotImplementedError
|
342
430
|
end
|
343
431
|
|
432
|
+
def self.hide_cursor
|
433
|
+
size = 100
|
434
|
+
visible = 0 # 0 means false
|
435
|
+
cursor_info = [size, visible].pack('Li')
|
436
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
437
|
+
end
|
438
|
+
|
439
|
+
def self.show_cursor
|
440
|
+
size = 100
|
441
|
+
visible = 1 # 1 means true
|
442
|
+
cursor_info = [size, visible].pack('Li')
|
443
|
+
@@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info)
|
444
|
+
end
|
445
|
+
|
344
446
|
def self.set_winch_handler(&handler)
|
345
447
|
@@winch_handler = handler
|
346
448
|
end
|