io-console 0.8.2-java → 0.9.1-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b840d62f5d17d3a367f0a978c107b39ecb08c59ca9a81d9bc9faf88041fbb909
4
- data.tar.gz: 8c4dc753789ff1a4c6e2b5b5d1c55e6d5c1d59e9fffe308fd824578c6ec30a0b
3
+ metadata.gz: e86817ae95f9a6fc3fa3301ea895706a3071f8e500c3c2dec9ef669dae510609
4
+ data.tar.gz: 433bb663068ef08dbc2a076c0d037d94872f2906652bd198ee950fff5c3a1bc7
5
5
  SHA512:
6
- metadata.gz: a2311dbec33c917c4033c6f1cad2e45e56c0d1e34b88ef5897d1d0e8e14003a9f4db6d15e0ffd24cd1e2ac3661a482729821f03e2c0a768bb9a67cd8276a18f5
7
- data.tar.gz: '0398f065e2c7de6be4c33291c3ea050622de33cb01364b0b7443a89c3aed0afd3ff6e13f615174ae1a6803fb9b3280c6288d7b615ee2a7e929224684105bf33c'
6
+ metadata.gz: ec7274caecc4e434b7a1e7afe30251d1b07f677c54d3865f3b341c008f96ca6a7d95faa7c87faacbbf65734ee7a0e23bbdfb469088349a03c1cacc5f82be3bee
7
+ data.tar.gz: a9b72f79b80a65809217ed7cdab1edeb01daa53ffe9647d42900960569f4db3e83bb869c80f8da8ee9b2c76d23a66b08cc5d879a373a9ac9d9f6bdd05c0c8a66
@@ -6,7 +6,7 @@ if RbConfig::CONFIG['host_os'].downcase =~ /darwin/ && FFI::Platform::ARCH !~ /#
6
6
  raise LoadError.new("native console on MacOS only supported on #{tested_platforms.join(', ')}")
7
7
  end
8
8
 
9
- module IO::LibC
9
+ module IO::Console::LibC
10
10
  extend FFI::Library
11
11
  ffi_lib FFI::Library::LIBC
12
12
 
@@ -170,3 +170,5 @@ module IO::LibC
170
170
  attach_function :tcflush, [ :int, :int ], :int
171
171
  attach_function :ioctl, [ :int, :ulong, :varargs ], :int
172
172
  end
173
+
174
+ require_relative 'native_console'
@@ -1,4 +1,9 @@
1
1
  # Methods common to all backend impls
2
+ require 'io/wait'
3
+
4
+ module IO::Console
5
+ end
6
+
2
7
  class IO
3
8
  # TODO: Windows version uses "conin$" and "conout$" instead of /dev/tty
4
9
  def self.console(sym = nil, *args)
@@ -62,12 +67,26 @@ class IO
62
67
  str.chomp
63
68
  end
64
69
 
70
+ def input_pending?
71
+ !wait_readable(0).nil?
72
+ end
73
+
74
+ def hide_cursor
75
+ write "\e[?25l"
76
+ self
77
+ end
78
+
79
+ def show_cursor
80
+ write "\e[?25h"
81
+ self
82
+ end
83
+
65
84
  def cursor
66
85
  raw do
67
86
  syswrite "\e[6n"
68
87
 
69
88
  return nil if getbyte != 0x1b
70
- return nil if getbyte != ?[.ord
89
+ return nil if getbyte != "[".ord
71
90
 
72
91
  num = 0
73
92
  result = []
@@ -6,7 +6,7 @@ unless FFI::Platform::ARCH =~ /#{tested_platforms.join('|')}/
6
6
  warn "native console only tested on #{tested_platforms.join(', ')}"
7
7
  end
8
8
 
9
- module IO::LibC
9
+ module IO::Console::LibC
10
10
  extend FFI::Library
11
11
  ffi_lib FFI::Library::LIBC
12
12
 
@@ -202,3 +202,5 @@ module IO::LibC
202
202
  attach_function :tcflush, [ :int, :int ], :int
203
203
  attach_function :ioctl, [ :int, :ulong, :varargs ], :int
204
204
  end
205
+
206
+ require_relative 'native_console'
@@ -1,15 +1,5 @@
1
- # Load appropriate native bits for BSD or Linux
2
- case RbConfig::CONFIG['host_os'].downcase
3
- when /darwin|openbsd|freebsd|netbsd/
4
- require_relative 'bsd_console'
5
- when /linux/
6
- require_relative 'linux_console'
7
- else
8
- raise LoadError.new("no native io/console support")
9
- end
10
-
11
1
  # Common logic that uses native calls for console
12
- class IO
2
+ module IO::Console
13
3
  def ttymode
14
4
  termios = LibC::Termios.new
15
5
  if LibC.tcgetattr(self.fileno, termios) != 0
@@ -18,21 +8,26 @@ class IO
18
8
 
19
9
  if block_given?
20
10
  yield tmp = termios.dup
21
- if LibC.tcsetattr(self.fileno, LibC::TCSANOW, tmp) != 0
22
- raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno)
23
- end
11
+ ttymode_set(tmp)
24
12
  end
25
13
  termios
26
14
  end
27
15
  private :ttymode
28
16
 
17
+ def ttymode_set(termios)
18
+ if LibC.tcsetattr(self.fileno, LibC::TCSANOW, termios) != 0
19
+ raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno)
20
+ end
21
+ end
22
+ private :ttymode_set
23
+
29
24
  def ttymode_yield(block, **opts, &setup)
30
25
  begin
31
26
  orig_termios = ttymode { |t| setup.call(t, **opts) }
32
27
  block.call(self)
33
28
  ensure
34
- if orig_termios && LibC.tcsetattr(self.fileno, LibC::TCSANOW, orig_termios) != 0
35
- raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno)
29
+ if orig_termios
30
+ ttymode_set(orig_termios)
36
31
  end
37
32
  end
38
33
  end
@@ -93,6 +88,46 @@ class IO
93
88
  ttymode_yield(block) { |t| t[:c_lflag] &= ~(TTY_ECHO) }
94
89
  end
95
90
 
91
+ class Mode
92
+ attr_reader :termios
93
+
94
+ def initialize(t)
95
+ @termios = t
96
+ end
97
+
98
+ def initialize_copy(m)
99
+ @termios = m.termios.dup
100
+ end
101
+
102
+ def echo=(echo)
103
+ if echo
104
+ @termios[:c_lflag] |= TTY_ECHO
105
+ else
106
+ @termios[:c_lflag] &= ~TTY_ECHO
107
+ end
108
+ end
109
+
110
+ def raw!(min: 1, time: nil, intr: nil)
111
+ TTY_RAW[@termios, min:, time:, intr:]
112
+ self
113
+ end
114
+
115
+ def raw(min: 1, time: nil, intr: nil)
116
+ new_mode = dup
117
+ TTY_RAW[new_mode.termios, min:, time:, intr:]
118
+ new_mode
119
+ end
120
+ end
121
+
122
+ def console_mode
123
+ Mode.new(ttymode)
124
+ end
125
+
126
+ def console_mode=(mode)
127
+ ttymode_set(mode.termios)
128
+ mode
129
+ end
130
+
96
131
  def winsize
97
132
  ws = LibC::Winsize.new
98
133
  if LibC.ioctl(self.fileno, LibC::TIOCGWINSZ, :pointer, ws.pointer) != 0
@@ -138,3 +173,7 @@ class IO
138
173
  raise SystemCallError.new("tcflush(TCIOFLUSH)", FFI.errno) unless LibC.tcflush(self.fileno, LibC::TCIOFLUSH) == 0
139
174
  end
140
175
  end
176
+
177
+ class IO
178
+ include Console
179
+ end
@@ -1,6 +1,10 @@
1
1
  # attempt to call stty; if failure, raise error
2
- `stty 2> /dev/null`
3
- if $?.exitstatus != 0
2
+ module IO::Console
3
+ STTY = %w[/usr/bin/stty /bin/stty].find {|path| File.executable?(path)}
4
+ end
5
+
6
+ unless IO::Console::STTY &&
7
+ system(IO::Console::STTY, out: File::NULL, err: %i[child out])
4
8
  raise "stty command returned nonzero exit status"
5
9
  end
6
10
 
@@ -8,46 +12,45 @@ warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE
8
12
 
9
13
  # Non-Windows assumes stty command is available
10
14
  class IO
11
- if RbConfig::CONFIG['host_os'].downcase =~ /linux/ && File.exists?("/proc/#{Process.pid}/fd")
12
- protected def _io_console_stty(*args)
13
- _io_console_stty_error { `stty #{args.join(' ')} < /proc/#{Process.pid}/fd/#{fileno}` }
14
- end
15
- else
16
- protected def _io_console_stty(*args)
17
- _io_console_stty_error { `stty #{args.join(' ')}` }
18
- end
19
- end
20
-
21
- protected def _io_console_stty_error
15
+ private def _io_console_stty(*args)
22
16
  # pre-check to catch non-tty filenos we can't stty against anyway
23
17
  raise Errno::ENOTTY, inspect if !tty?
24
18
 
25
- result = yield
26
-
27
- case result
28
- when /Inappropriate ioctl for device/
29
- raise Errno.ENOTTY, inspect
19
+ IO.pipe do |re, we|
20
+ input = dup # workaround for JRuby
21
+ IO.popen([Console::STTY, *args, in: input, err: we], &:read)
22
+ ensure
23
+ input&.close
24
+ unless $?.success?
25
+ we.close
26
+ error = re.read
27
+ case error
28
+ when /Inappropriate ioctl for device/
29
+ raise Errno::ENOTTY, inspect
30
+ end
31
+ raise "stty command failed: #{error.chomp}"
32
+ end
30
33
  end
31
-
32
- result
33
34
  end
34
35
 
35
36
  def raw(*, min: 1, time: nil, intr: nil)
36
- saved = _io_console_stty('-g raw')
37
+ saved = _io_console_stty('-g')
38
+ _io_console_stty('raw')
37
39
  yield self
38
40
  ensure
39
- _io_console_stty(saved)
41
+ _io_console_stty(saved) if saved
40
42
  end
41
43
 
42
44
  def raw!(*)
43
- stty('raw')
45
+ _io_console_stty('raw')
44
46
  end
45
47
 
46
48
  def cooked(*)
47
- saved = _io_console_stty('-g', '-raw')
49
+ saved = _io_console_stty('-g')
50
+ _io_console_stty('-raw')
48
51
  yield self
49
52
  ensure
50
- _io_console_stty(saved)
53
+ _io_console_stty(saved) if saved
51
54
  end
52
55
 
53
56
  def cooked!(*)
@@ -63,10 +66,11 @@ class IO
63
66
  end
64
67
 
65
68
  def noecho
66
- saved = _io_console_stty('-g', '-echo')
69
+ saved = _io_console_stty('-g')
70
+ _io_console_stty('-echo')
67
71
  yield self
68
72
  ensure
69
- _io_console_stty(saved)
73
+ _io_console_stty(saved) if saved
70
74
  end
71
75
 
72
76
  # Not all systems return same format of stty -a output
@@ -74,7 +78,7 @@ class IO
74
78
  UBUNTU = 'rows (?<rows>\d+); columns (?<columns>\d+)'
75
79
 
76
80
  def winsize
77
- match = _io_console_stty('-a').match(/#{IEEE_STD_1003_2}|#{UBUNTU}/)
81
+ match = _io_console_stty('-a').match(/#{IEEE_STD_1003_2}|#{UBUNTU}/o)
78
82
  [match[:rows].to_i, match[:columns].to_i]
79
83
  end
80
84
 
@@ -1,3 +1,5 @@
1
- class IO::ConsoleMode
2
- VERSION = "0.8.2"
1
+ class IO
2
+ module Console
3
+ VERSION = "0.9.1"
4
+ end
3
5
  end
@@ -0,0 +1,293 @@
1
+ require 'ffi'
2
+
3
+ module IO::Console::Windows
4
+ STD_INPUT_HANDLE = -10
5
+ STD_OUTPUT_HANDLE = -11
6
+ WAIT_OBJECT_0 = 0
7
+ WAIT_TIMEOUT = 258
8
+ ENABLE_WRAP_AT_EOL_OUTPUT = 2
9
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
10
+
11
+ module Native
12
+ extend FFI::Library
13
+ ffi_convention :stdcall
14
+ ffi_lib 'kernel32'
15
+ attach_function :GetStdHandle, [:int32], :pointer
16
+ attach_function :GetConsoleMode, [:pointer, :pointer], :int
17
+ attach_function :SetConsoleMode, [:pointer, :uint32], :int
18
+ attach_function :WaitForSingleObject, [:pointer, :uint32], :uint32
19
+ attach_function :ReadConsoleInputW,
20
+ [:pointer, :pointer, :uint32, :pointer], :int
21
+ attach_function :GetFileType, [:pointer], :uint32
22
+ attach_function :GetFileInformationByHandleEx,
23
+ [:pointer, :int, :pointer, :uint32], :int
24
+ attach_function :GetConsoleScreenBufferInfo, [:pointer, :pointer], :int
25
+ attach_function :SetConsoleCursorPosition, [:pointer, :uint32], :int
26
+ attach_function :FillConsoleOutputCharacterW,
27
+ [:pointer, :uint16, :uint32, :uint32, :pointer], :int
28
+ attach_function :FillConsoleOutputAttribute,
29
+ [:pointer, :uint16, :uint32, :uint32, :pointer], :int
30
+ attach_function :GetConsoleCursorInfo, [:pointer, :pointer], :int
31
+ attach_function :SetConsoleCursorInfo, [:pointer, :pointer], :int
32
+ end
33
+
34
+ module CRT
35
+ extend FFI::Library
36
+ ffi_convention :cdecl
37
+ ffi_lib 'msvcrt'
38
+ attach_function :_kbhit, [], :int
39
+ end
40
+
41
+ GetStdHandle = Native.method(:GetStdHandle)
42
+ GetConsoleMode = Native.method(:GetConsoleMode)
43
+ SetConsoleMode = Native.method(:SetConsoleMode)
44
+ WaitForSingleObject = Native.method(:WaitForSingleObject)
45
+ ReadConsoleInputW = Native.method(:ReadConsoleInputW)
46
+ GetFileType = Native.method(:GetFileType)
47
+ GetFileInformationByHandleEx = Native.method(:GetFileInformationByHandleEx)
48
+ GetConsoleScreenBufferInfo = Native.method(:GetConsoleScreenBufferInfo)
49
+ SetConsoleCursorPosition = Native.method(:SetConsoleCursorPosition)
50
+ FillConsoleOutputCharacter = Native.method(:FillConsoleOutputCharacterW)
51
+ FillConsoleOutputAttribute = Native.method(:FillConsoleOutputAttribute)
52
+ GetConsoleCursorInfo = Native.method(:GetConsoleCursorInfo)
53
+ SetConsoleCursorInfo = Native.method(:SetConsoleCursorInfo)
54
+ Kbhit = CRT.method(:_kbhit)
55
+
56
+ INPUT_HANDLE = GetStdHandle.call(STD_INPUT_HANDLE)
57
+ OUTPUT_HANDLE = GetStdHandle.call(STD_OUTPUT_HANDLE)
58
+
59
+ module_function
60
+
61
+ def handle(io)
62
+ io.equal?(STDIN) ? INPUT_HANDLE : OUTPUT_HANDLE
63
+ end
64
+
65
+ def console_mode(io)
66
+ buffer = "\0" * 4
67
+ raise SystemCallError, 'GetConsoleMode' if GetConsoleMode.call(handle(io), buffer) == 0
68
+ buffer.unpack1('L')
69
+ end
70
+
71
+ def set_console_mode(io, mode)
72
+ raise SystemCallError, 'SetConsoleMode' if SetConsoleMode.call(handle(io), mode) == 0
73
+ end
74
+
75
+ def screen_buffer_info(io)
76
+ buffer = "\0" * 22
77
+ raise SystemCallError, 'GetConsoleScreenBufferInfo' if GetConsoleScreenBufferInfo.call(handle(io), buffer) == 0
78
+ buffer.unpack('s9')
79
+ end
80
+
81
+ def coordinate(x, y)
82
+ (y & 0xffff) << 16 | (x & 0xffff)
83
+ end
84
+ end
85
+
86
+ module IO::Console::Windows::TTY
87
+ def tty?(*types)
88
+ return super() if types.empty?
89
+
90
+ default = msys = cygwin = false
91
+ types.each do |type|
92
+ case type
93
+ when nil
94
+ default = true
95
+ when :any
96
+ default = msys = cygwin = true
97
+ when :msys
98
+ msys = true
99
+ when :cygwin
100
+ cygwin = true
101
+ when Symbol
102
+ raise ArgumentError, "unknown tty type: #{type.inspect}"
103
+ else
104
+ raise TypeError, "expected Symbol, got #{type.class}"
105
+ end
106
+ end
107
+
108
+ return true if default && super()
109
+ (msys || cygwin) && msys_tty?(msys, cygwin)
110
+ end
111
+ alias isatty tty?
112
+
113
+ private def msys_tty?(msys, cygwin)
114
+ windows = IO::Console::Windows
115
+ handle = windows.handle(self)
116
+ return false unless windows::GetFileType.call(handle) == 3
117
+ buffer = "\0" * 1024
118
+ return false if windows::GetFileInformationByHandleEx.call(handle, 2, buffer, 1022) == 0
119
+ length = buffer.unpack1('L')
120
+ name = buffer[4, length].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace)
121
+ return false unless (msys && name.start_with?('\\msys-')) || (cygwin && name.start_with?('\\cygwin-'))
122
+ name.include?('-pty')
123
+ end
124
+ end
125
+
126
+ IO.prepend(IO::Console::Windows::TTY)
127
+
128
+ class IO::ConsoleMode
129
+ def initialize(mode)
130
+ @mode = mode
131
+ end
132
+
133
+ def virtual_terminal_processing?
134
+ @mode & IO::Console::Windows::ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0
135
+ end
136
+
137
+ def virtual_terminal_processing=(enabled)
138
+ set_flag(IO::Console::Windows::ENABLE_VIRTUAL_TERMINAL_PROCESSING, enabled)
139
+ end
140
+
141
+ def wrap_at_eol_output?
142
+ @mode & IO::Console::Windows::ENABLE_WRAP_AT_EOL_OUTPUT != 0
143
+ end
144
+
145
+ def wrap_at_eol_output=(enabled)
146
+ set_flag(IO::Console::Windows::ENABLE_WRAP_AT_EOL_OUTPUT, enabled)
147
+ end
148
+
149
+ private def set_flag(flag, enabled)
150
+ enabled ? @mode |= flag : @mode &= ~flag
151
+ self
152
+ end
153
+
154
+ private def to_i
155
+ @mode
156
+ end
157
+ end
158
+
159
+ class IO
160
+ def console_mode
161
+ IO::ConsoleMode.new(IO::Console::Windows.console_mode(self))
162
+ end
163
+
164
+ def console_mode=(mode)
165
+ IO::Console::Windows.set_console_mode(self, mode.__send__(:to_i))
166
+ mode
167
+ end
168
+
169
+ def input_pending?
170
+ windows = IO::Console::Windows
171
+ return windows::Kbhit.call != 0 if windows::GetConsoleMode.call(windows.handle(self), "\0" * 4) != 0
172
+ respond_to?(:wait_readable) && !!wait_readable(0)
173
+ end
174
+
175
+ def console_input_events(max_events = 1, timeout: nil)
176
+ raise ArgumentError, 'max_events must be positive' unless max_events > 0
177
+ raise ArgumentError, 'time interval must not be negative' if timeout && timeout < 0
178
+
179
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout if timeout
180
+ loop do
181
+ wait = deadline && deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
182
+ return [] if wait && wait <= 0
183
+ milliseconds = wait ? [[(wait * 1000).ceil, 100].min, 0].max : 100
184
+ windows = IO::Console::Windows
185
+ result = windows::WaitForSingleObject.call(windows.handle(self), milliseconds)
186
+ break if result == windows::WAIT_OBJECT_0
187
+ return [] if result != windows::WAIT_TIMEOUT
188
+ return [] if deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
189
+ end
190
+
191
+ records = "\0" * 20 * max_events
192
+ count = "\0" * 4
193
+ windows = IO::Console::Windows
194
+ if windows::ReadConsoleInputW.call(windows.handle(self), records, max_events, count) == 0
195
+ raise SystemCallError, 'ReadConsoleInputW'
196
+ end
197
+ count.unpack1('L').times.map do |index|
198
+ record = records[index * 20, 20]
199
+ event_type = record.unpack1('S')
200
+ case event_type
201
+ when 1
202
+ key_down, repeat_count, virtual_key_code, virtual_scan_code,
203
+ unicode_char, control_key_state = record[4, 16].unpack('LS4L')
204
+ {
205
+ type: :key, key_down: key_down != 0, repeat_count: repeat_count,
206
+ virtual_key_code: virtual_key_code, virtual_scan_code: virtual_scan_code,
207
+ unicode_char: unicode_char, control_key_state: control_key_state,
208
+ }
209
+ when 2
210
+ x, y, button_state, control_key_state, event_flags = record[4, 16].unpack('s2L3')
211
+ {type: :mouse, position: [y, x], button_state: button_state,
212
+ control_key_state: control_key_state, event_flags: event_flags}
213
+ when 4
214
+ x, y = record[4, 4].unpack('s2')
215
+ {type: :window_buffer_size, size: [y, x]}
216
+ when 8
217
+ {type: :menu, command_id: record[4, 4].unpack1('L')}
218
+ when 16
219
+ {type: :focus, set_focus: record[4, 4].unpack1('L') != 0}
220
+ else
221
+ {type: event_type}
222
+ end
223
+ end
224
+ end
225
+
226
+ def winsize
227
+ width, _, _, _, _, _, top, _, bottom = IO::Console::Windows.screen_buffer_info(self)
228
+ [bottom - top + 1, width]
229
+ end
230
+
231
+ def cursor
232
+ _, _, x, y, _, _, top, = IO::Console::Windows.screen_buffer_info(self)
233
+ [y - top, x]
234
+ end
235
+
236
+ def goto(row, column)
237
+ windows = IO::Console::Windows
238
+ _, _, _, _, _, _, top, = windows.screen_buffer_info(self)
239
+ position = windows.coordinate(column, row + top)
240
+ raise SystemCallError, 'SetConsoleCursorPosition' if windows::SetConsoleCursorPosition.call(windows.handle(self), position) == 0
241
+ self
242
+ end
243
+
244
+ def goto_column(column)
245
+ row, = cursor
246
+ goto(row, column)
247
+ end
248
+
249
+ def erase_line(mode)
250
+ raise ArgumentError, 'invalid line erase mode' unless (0..2).cover?(mode)
251
+ windows = IO::Console::Windows
252
+ width, _, x, y, attributes, = windows.screen_buffer_info(self)
253
+ start = mode == 0 ? x : 0
254
+ length = mode == 0 ? width - x : mode == 1 ? x + 1 : width
255
+ position = windows.coordinate(start, y)
256
+ written = "\0" * 4
257
+ windows::FillConsoleOutputCharacter.call(windows.handle(self), 0x20, length, position, written)
258
+ windows::FillConsoleOutputAttribute.call(windows.handle(self), attributes, length, position, written)
259
+ self
260
+ end
261
+
262
+ def clear_screen
263
+ windows = IO::Console::Windows
264
+ width, _, _, _, attributes, _, top, _, bottom = windows.screen_buffer_info(self)
265
+ length = width * (bottom - top + 1)
266
+ position = windows.coordinate(0, top)
267
+ written = "\0" * 4
268
+ windows::FillConsoleOutputCharacter.call(windows.handle(self), 0x20, length, position, written)
269
+ windows::FillConsoleOutputAttribute.call(windows.handle(self), attributes, length, position, written)
270
+ windows::SetConsoleCursorPosition.call(windows.handle(self), position)
271
+ self
272
+ end
273
+
274
+ def hide_cursor
275
+ set_cursor_visibility(false)
276
+ end
277
+
278
+ def show_cursor
279
+ set_cursor_visibility(true)
280
+ end
281
+
282
+ private def set_cursor_visibility(visible)
283
+ info = "\0" * 8
284
+ windows = IO::Console::Windows
285
+ handle = windows.handle(self)
286
+ raise SystemCallError, 'GetConsoleCursorInfo' if windows::GetConsoleCursorInfo.call(handle, info) == 0
287
+ size, = info.unpack('L2')
288
+ info = [size, visible ? 1 : 0].pack('L2')
289
+ raise SystemCallError, 'SetConsoleCursorInfo' if windows::SetConsoleCursorInfo.call(handle, info) == 0
290
+ self
291
+ end
292
+
293
+ end
@@ -0,0 +1,26 @@
1
+ module IO::Console::Windows
2
+ VK_TAB = 0x09
3
+ VK_RETURN = 0x0d
4
+ VK_SHIFT = 0x10
5
+ VK_CONTROL = 0x11
6
+ VK_MENU = 0x12
7
+ VK_END = 0x23
8
+ VK_HOME = 0x24
9
+ VK_LEFT = 0x25
10
+ VK_UP = 0x26
11
+ VK_RIGHT = 0x27
12
+ VK_DOWN = 0x28
13
+ VK_DELETE = 0x2e
14
+ VK_DIVIDE = 0x6f
15
+ VK_LMENU = 0xa4
16
+
17
+ RIGHT_ALT_PRESSED = 0x0001
18
+ LEFT_ALT_PRESSED = 0x0002
19
+ RIGHT_CTRL_PRESSED = 0x0004
20
+ LEFT_CTRL_PRESSED = 0x0008
21
+ SHIFT_PRESSED = 0x0010
22
+ NUMLOCK_ON = 0x0020
23
+ SCROLLLOCK_ON = 0x0040
24
+ CAPSLOCK_ON = 0x0080
25
+ ENHANCED_KEY = 0x0100
26
+ end
@@ -24,51 +24,40 @@ require 'rbconfig'
24
24
  require_relative 'console/version'
25
25
  require_relative 'console/common'
26
26
 
27
- case RbConfig::CONFIG['host_os']
28
- when /darwin|openbsd|freebsd|netbsd|linux/i
29
- # If Linux or BSD, try to load the native version
30
-
31
- begin
32
-
33
- # Attempt to load the native Linux and BSD console logic
34
- require_relative 'console/native_console'
35
-
36
- rescue Exception => ex
37
-
38
- warn "failed to load native console support: #{ex}" if $VERBOSE
39
-
40
- else
41
-
42
- # Native ready.
43
- ready = true
44
-
27
+ class IO
28
+ module Console
29
+ class Mode
30
+ VERSION = Console::VERSION
31
+ deprecate_constant :VERSION
32
+ end
45
33
  end
46
34
 
47
- when /mswin|win32|ming/i
48
- # If Windows, stty is not possible, always use the stub version
49
-
50
- ready = false
51
-
35
+ ConsoleMode = Console::Mode
36
+ deprecate_constant :ConsoleMode
52
37
  end
53
38
 
54
- if ready.nil?
55
- # Native is not ready, try to use stty
56
-
57
- begin
58
-
59
- require_relative 'console/stty_console'
60
- ready = true
61
-
62
- rescue Exception => ex2
63
-
64
- warn "failed to load stty console support: #{ex2}" if $VERBOSE
65
- ready = false
66
-
67
- end
39
+ libs = []
40
+ # If Linux or BSD, try to load the native version
41
+ case RbConfig::CONFIG['host_os'].downcase
42
+ when /darwin|openbsd|freebsd|netbsd/
43
+ libs << 'bsd' << 'stty'
44
+ when /linux/
45
+ libs << 'linux' << 'stty'
46
+ when /mswin|win32|ming/i
47
+ require_relative 'console/windows_constants'
48
+ require_relative 'console/windows_console'
49
+ return
50
+ else
51
+ libs << 'stty'
68
52
  end
69
53
 
70
- unless ready
71
- # If still not ready, just use stubbed version
72
-
73
- require_relative 'console/stub_console'
54
+ return if libs.any? do |lib|
55
+ require_relative "console/#{lib}_console"
56
+ rescue Exception => ex
57
+ warn "failed to load #{lib} console support: #{ex}" if $VERBOSE
58
+ else
59
+ true
74
60
  end
61
+
62
+ # If still not ready, just use stubbed version
63
+ require_relative "console/stub_console"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-console
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.1
5
5
  platform: java
6
6
  authors:
7
7
  - Nobu Nakada
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-14 00:00:00.000000000 Z
10
+ date: 2026-08-08 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: add console capabilities to IO instances.
13
13
  email: nobu@ruby-lang.org
@@ -27,6 +27,8 @@ files:
27
27
  - lib/ffi/io/console/stty_console.rb
28
28
  - lib/ffi/io/console/stub_console.rb
29
29
  - lib/ffi/io/console/version.rb
30
+ - lib/ffi/io/console/windows_console.rb
31
+ - lib/ffi/io/console/windows_constants.rb
30
32
  - lib/io/console/size.rb
31
33
  homepage: https://github.com/ruby/io-console
32
34
  licenses: