diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18324a4..2e8777e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: if: ${{ startsWith(github.repository, 'ruby/') || github.event_name != 'schedule' }} uses: ruby/actions/.github/workflows/ruby_versions.yml@master with: - engine: cruby + engine: all min_version: 2.6 # from `required_ruby_version` in io-console.gemspec test: @@ -25,9 +25,15 @@ jobs: include: - ruby: mswin os: windows-latest + - ruby: jruby-head + os: macos-15-intel + exclude: - ruby: jruby + os: windows-latest - ruby: jruby-head + os: windows-latest - ruby: truffleruby + os: windows-latest runs-on: ${{ matrix.os || 'ubuntu-latest' }} steps: - name: git config diff --git a/Rakefile b/Rakefile index ce1f6a3..863c1cf 100644 --- a/Rakefile +++ b/Rakefile @@ -32,11 +32,11 @@ task :test => ffi_version_file if RUBY_ENGINE == "jruby" Rake::TestTask.new(:test) do |t| if extask t.libs = [extask.lib_dir.chomp("/"+File.dirname(name))] + elsif RUBY_ENGINE == "jruby" + t.libs.unshift "lib/ffi" end - t.libs.unshift "lib/ffi" if RUBY_ENGINE == "jruby" t.libs << "test/lib" t.ruby_opts << "-rhelper" - t.options = "--ignore-name=TestIO_Console#test_bad_keyword" if RUBY_ENGINE == "jruby" t.test_files = FileList["test/**/test_*.rb"] end diff --git a/lib/ffi/io/console/stty_console.rb b/lib/ffi/io/console/stty_console.rb index 9916a45..593ad13 100644 --- a/lib/ffi/io/console/stty_console.rb +++ b/lib/ffi/io/console/stty_console.rb @@ -3,13 +3,44 @@ module IO::Console STTY = %w[/usr/bin/stty /bin/stty].find {|path| File.executable?(path)} end -unless IO::Console::STTY && - system(IO::Console::STTY, out: File::NULL, err: %i[child out]) - raise "stty command returned nonzero exit status" -end +raise LoadError, "stty command not found" unless IO::Console::STTY warn "io/console on JRuby shells out to stty for most operations" if $VERBOSE +class IO::Console::Mode + def initialize(saved) + @saved = saved + @args = [] + end + + def initialize_copy(mode) + super + @saved = mode.__send__(:saved).dup + @args = mode.__send__(:args).dup + end + + def echo=(echo) + @args << (echo ? 'echo' : '-echo') + echo + end + + def raw(min: 1, time: nil, intr: nil) + dup.raw!(min:, time:, intr:) + end + + def raw!(min: 1, time: nil, intr: nil) + @args << 'raw' + @args.push('min', min.to_s) if min >= 0 + @args.push('time', ((time || 0) * 10).to_i.to_s) + @args.concat(%w[brkint isig opost]) if intr + self + end + + private + + attr_reader :saved, :args +end + # Non-Windows assumes stty command is available class IO private def _io_console_stty(*args) @@ -34,11 +65,11 @@ class IO end def raw(*, min: 1, time: nil, intr: nil) - saved = _io_console_stty('-g') - _io_console_stty('raw') + saved = console_mode + self.console_mode = saved.raw(min:, time:, intr:) yield self ensure - _io_console_stty(saved) if saved + self.console_mode = saved if saved end def raw!(*) @@ -73,6 +104,15 @@ def noecho _io_console_stty(saved) if saved end + def console_mode + Console::Mode.new(_io_console_stty('-g').chomp) + end + + def console_mode=(mode) + _io_console_stty(mode.__send__(:saved), *mode.__send__(:args)) + mode + end + # Not all systems return same format of stty -a output IEEE_STD_1003_2 = '(?\d+) rows; (?\d+) columns' UBUNTU = 'rows (?\d+); columns (?\d+)' @@ -96,7 +136,7 @@ def winsize=(size) row, col, _, _ = size - _io_console_stty("rows #{row} cols #{col}") + _io_console_stty('rows', row.to_s, 'cols', col.to_s) end def iflush diff --git a/test/io/console/test_io_console.rb b/test/io/console/test_io_console.rb index e88cc03..173e6a2 100644 --- a/test/io/console/test_io_console.rb +++ b/test/io/console/test_io_console.rb @@ -369,6 +369,8 @@ def test_getpass end def test_iflush + pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + helper {|m, s| m.print "a" s.iflush @@ -390,6 +392,8 @@ def test_oflush end def test_ioflush + pend "stty cannot flush terminal queues" if IO.private_method_defined?(:_io_console_stty) + helper {|m, s| m.print "a" s.ioflush