From b7ac2e04e94a636b66b19ee04d912dcdb70404b1 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 9 Aug 2026 18:08:27 +0900 Subject: [PATCH 1/4] Move JRuby implementation under jruby/lib --- .gitignore | 2 +- Rakefile | 10 ++++----- io-console.gemspec | 22 +++++++++---------- {lib/ffi => jruby/lib}/io/console.rb | 0 .../lib}/io/console/bsd_console.rb | 0 {lib/ffi => jruby/lib}/io/console/common.rb | 0 .../lib}/io/console/linux_console.rb | 0 .../lib}/io/console/native_console.rb | 0 .../lib}/io/console/stty_console.rb | 0 .../lib}/io/console/stub_console.rb | 0 .../lib}/io/console/windows_console.rb | 0 .../lib}/io/console/windows_constants.rb | 0 12 files changed, 17 insertions(+), 17 deletions(-) rename {lib/ffi => jruby/lib}/io/console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/bsd_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/common.rb (100%) rename {lib/ffi => jruby/lib}/io/console/linux_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/native_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/stty_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/stub_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/windows_console.rb (100%) rename {lib/ffi => jruby/lib}/io/console/windows_constants.rb (100%) diff --git a/.gitignore b/.gitignore index 630ca93..69da51b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ Gemfile.lock *.bundle *.dll *.so -lib/ffi/io/console/version.rb +jruby/lib/io/console/version.rb diff --git a/Rakefile b/Rakefile index 863c1cf..169d954 100644 --- a/Rakefile +++ b/Rakefile @@ -12,8 +12,8 @@ if RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby" task :test => :compile end -ffi_version_file = "lib/ffi/#{name}/version.rb" -task ffi_version_file => "#{name.tr('/', '-')}.gemspec" do |t| +jruby_version_file = "jruby/lib/#{name}/version.rb" +task jruby_version_file => "#{name.tr('/', '-')}.gemspec" do |t| version = <<~RUBY class IO module Console @@ -26,14 +26,14 @@ task ffi_version_file => "#{name.tr('/', '-')}.gemspec" do |t| end end -task :build => ffi_version_file -task :test => ffi_version_file if RUBY_ENGINE == "jruby" +task :build => jruby_version_file +task :test => jruby_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" + t.libs.unshift "jruby/lib" end t.libs << "test/lib" t.ruby_opts << "-rhelper" diff --git a/io-console.gemspec b/io-console.gemspec index 8a5093c..884d2d5 100644 --- a/io-console.gemspec +++ b/io-console.gemspec @@ -36,18 +36,18 @@ Gem::Specification.new do |s| if Gem::Platform === s.platform and s.platform =~ 'java' s.files.delete_if {|f| f.start_with?("ext/")} s.extensions.clear - s.require_paths.unshift('lib/ffi') + s.require_paths.unshift('jruby/lib') s.files.concat(%w[ - lib/ffi/io/console.rb - lib/ffi/io/console/bsd_console.rb - lib/ffi/io/console/common.rb - lib/ffi/io/console/linux_console.rb - lib/ffi/io/console/native_console.rb - lib/ffi/io/console/stty_console.rb - lib/ffi/io/console/stub_console.rb - lib/ffi/io/console/windows_constants.rb - lib/ffi/io/console/windows_console.rb - lib/ffi/io/console/version.rb + jruby/lib/io/console.rb + jruby/lib/io/console/bsd_console.rb + jruby/lib/io/console/common.rb + jruby/lib/io/console/linux_console.rb + jruby/lib/io/console/native_console.rb + jruby/lib/io/console/stty_console.rb + jruby/lib/io/console/stub_console.rb + jruby/lib/io/console/windows_constants.rb + jruby/lib/io/console/windows_console.rb + jruby/lib/io/console/version.rb ]) end diff --git a/lib/ffi/io/console.rb b/jruby/lib/io/console.rb similarity index 100% rename from lib/ffi/io/console.rb rename to jruby/lib/io/console.rb diff --git a/lib/ffi/io/console/bsd_console.rb b/jruby/lib/io/console/bsd_console.rb similarity index 100% rename from lib/ffi/io/console/bsd_console.rb rename to jruby/lib/io/console/bsd_console.rb diff --git a/lib/ffi/io/console/common.rb b/jruby/lib/io/console/common.rb similarity index 100% rename from lib/ffi/io/console/common.rb rename to jruby/lib/io/console/common.rb diff --git a/lib/ffi/io/console/linux_console.rb b/jruby/lib/io/console/linux_console.rb similarity index 100% rename from lib/ffi/io/console/linux_console.rb rename to jruby/lib/io/console/linux_console.rb diff --git a/lib/ffi/io/console/native_console.rb b/jruby/lib/io/console/native_console.rb similarity index 100% rename from lib/ffi/io/console/native_console.rb rename to jruby/lib/io/console/native_console.rb diff --git a/lib/ffi/io/console/stty_console.rb b/jruby/lib/io/console/stty_console.rb similarity index 100% rename from lib/ffi/io/console/stty_console.rb rename to jruby/lib/io/console/stty_console.rb diff --git a/lib/ffi/io/console/stub_console.rb b/jruby/lib/io/console/stub_console.rb similarity index 100% rename from lib/ffi/io/console/stub_console.rb rename to jruby/lib/io/console/stub_console.rb diff --git a/lib/ffi/io/console/windows_console.rb b/jruby/lib/io/console/windows_console.rb similarity index 100% rename from lib/ffi/io/console/windows_console.rb rename to jruby/lib/io/console/windows_console.rb diff --git a/lib/ffi/io/console/windows_constants.rb b/jruby/lib/io/console/windows_constants.rb similarity index 100% rename from lib/ffi/io/console/windows_constants.rb rename to jruby/lib/io/console/windows_constants.rb From 8b57c65011c0919bdb3a9fb111e2f330754861bc Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 9 Aug 2026 18:25:53 +0900 Subject: [PATCH 2/4] Organize JRuby console implementations Group implementations by backend and isolate platform-specific constants. --- io-console.gemspec | 14 ++++++------- jruby/lib/io/console.rb | 21 ++++++++++--------- .../{native_console.rb => backend/ffi.rb} | 0 .../{stty_console.rb => backend/stty.rb} | 0 .../{stub_console.rb => backend/stub.rb} | 0 .../windows.rb} | 0 .../{bsd_console.rb => constants/bsd.rb} | 2 -- .../{linux_console.rb => constants/linux.rb} | 2 -- .../windows.rb} | 0 9 files changed, 18 insertions(+), 21 deletions(-) rename jruby/lib/io/console/{native_console.rb => backend/ffi.rb} (100%) rename jruby/lib/io/console/{stty_console.rb => backend/stty.rb} (100%) rename jruby/lib/io/console/{stub_console.rb => backend/stub.rb} (100%) rename jruby/lib/io/console/{windows_console.rb => backend/windows.rb} (100%) rename jruby/lib/io/console/{bsd_console.rb => constants/bsd.rb} (99%) rename jruby/lib/io/console/{linux_console.rb => constants/linux.rb} (99%) rename jruby/lib/io/console/{windows_constants.rb => constants/windows.rb} (100%) diff --git a/io-console.gemspec b/io-console.gemspec index 884d2d5..ee02747 100644 --- a/io-console.gemspec +++ b/io-console.gemspec @@ -39,14 +39,14 @@ Gem::Specification.new do |s| s.require_paths.unshift('jruby/lib') s.files.concat(%w[ jruby/lib/io/console.rb - jruby/lib/io/console/bsd_console.rb + jruby/lib/io/console/backend/ffi.rb + jruby/lib/io/console/backend/stty.rb + jruby/lib/io/console/backend/stub.rb + jruby/lib/io/console/backend/windows.rb jruby/lib/io/console/common.rb - jruby/lib/io/console/linux_console.rb - jruby/lib/io/console/native_console.rb - jruby/lib/io/console/stty_console.rb - jruby/lib/io/console/stub_console.rb - jruby/lib/io/console/windows_constants.rb - jruby/lib/io/console/windows_console.rb + jruby/lib/io/console/constants/bsd.rb + jruby/lib/io/console/constants/linux.rb + jruby/lib/io/console/constants/windows.rb jruby/lib/io/console/version.rb ]) end diff --git a/jruby/lib/io/console.rb b/jruby/lib/io/console.rb index 5cedfcb..724114b 100644 --- a/jruby/lib/io/console.rb +++ b/jruby/lib/io/console.rb @@ -36,28 +36,29 @@ class Mode deprecate_constant :ConsoleMode end -libs = [] +backends = [] # If Linux or BSD, try to load the native version case RbConfig::CONFIG['host_os'].downcase when /darwin|openbsd|freebsd|netbsd/ - libs << 'bsd' << 'stty' + backends << %w[ffi bsd] << %w[stty] when /linux/ - libs << 'linux' << 'stty' + backends << %w[ffi linux] << %w[stty] when /mswin|win32|ming/i - require_relative 'console/windows_constants' - require_relative 'console/windows_console' + require_relative 'console/constants/windows' + require_relative 'console/backend/windows' return else - libs << 'stty' + backends << %w[stty] end -return if libs.any? do |lib| - require_relative "console/#{lib}_console" +return if backends.any? do |backend, constants| + require_relative "console/constants/#{constants}" if constants + require_relative "console/backend/#{backend}" rescue Exception => ex - warn "failed to load #{lib} console support: #{ex}" if $VERBOSE + warn "failed to load #{backend} console support: #{ex}" if $VERBOSE else true end # If still not ready, just use stubbed version -require_relative "console/stub_console" +require_relative 'console/backend/stub' diff --git a/jruby/lib/io/console/native_console.rb b/jruby/lib/io/console/backend/ffi.rb similarity index 100% rename from jruby/lib/io/console/native_console.rb rename to jruby/lib/io/console/backend/ffi.rb diff --git a/jruby/lib/io/console/stty_console.rb b/jruby/lib/io/console/backend/stty.rb similarity index 100% rename from jruby/lib/io/console/stty_console.rb rename to jruby/lib/io/console/backend/stty.rb diff --git a/jruby/lib/io/console/stub_console.rb b/jruby/lib/io/console/backend/stub.rb similarity index 100% rename from jruby/lib/io/console/stub_console.rb rename to jruby/lib/io/console/backend/stub.rb diff --git a/jruby/lib/io/console/windows_console.rb b/jruby/lib/io/console/backend/windows.rb similarity index 100% rename from jruby/lib/io/console/windows_console.rb rename to jruby/lib/io/console/backend/windows.rb diff --git a/jruby/lib/io/console/bsd_console.rb b/jruby/lib/io/console/constants/bsd.rb similarity index 99% rename from jruby/lib/io/console/bsd_console.rb rename to jruby/lib/io/console/constants/bsd.rb index 4ab9369..3c4c18d 100644 --- a/jruby/lib/io/console/bsd_console.rb +++ b/jruby/lib/io/console/constants/bsd.rb @@ -170,5 +170,3 @@ class Winsize < FFI::Struct attach_function :tcflush, [ :int, :int ], :int attach_function :ioctl, [ :int, :ulong, :varargs ], :int end - -require_relative 'native_console' diff --git a/jruby/lib/io/console/linux_console.rb b/jruby/lib/io/console/constants/linux.rb similarity index 99% rename from jruby/lib/io/console/linux_console.rb rename to jruby/lib/io/console/constants/linux.rb index 02ef924..4df4574 100644 --- a/jruby/lib/io/console/linux_console.rb +++ b/jruby/lib/io/console/constants/linux.rb @@ -202,5 +202,3 @@ class Winsize < FFI::Struct attach_function :tcflush, [ :int, :int ], :int attach_function :ioctl, [ :int, :ulong, :varargs ], :int end - -require_relative 'native_console' diff --git a/jruby/lib/io/console/windows_constants.rb b/jruby/lib/io/console/constants/windows.rb similarity index 100% rename from jruby/lib/io/console/windows_constants.rb rename to jruby/lib/io/console/constants/windows.rb From 0ec2d9d45acb2c6b60dac59724f16fbf0a99e52b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 9 Aug 2026 20:52:53 +0900 Subject: [PATCH 3/4] Separate JRuby FFI bindings from constants Keep platform constants reusable without initializing an FFI backend. --- io-console.gemspec | 4 +- jruby/lib/io/console.rb | 14 ++-- .../backend/{ffi.rb => ffi/termios.rb} | 59 +++++++++++++++ .../io/console/backend/{ => ffi}/windows.rb | 7 -- jruby/lib/io/console/constants/bsd.rb | 71 +++---------------- jruby/lib/io/console/constants/linux.rb | 49 ++----------- jruby/lib/io/console/constants/windows.rb | 7 ++ 7 files changed, 88 insertions(+), 123 deletions(-) rename jruby/lib/io/console/backend/{ffi.rb => ffi/termios.rb} (70%) rename jruby/lib/io/console/backend/{ => ffi}/windows.rb (98%) diff --git a/io-console.gemspec b/io-console.gemspec index ee02747..b10e423 100644 --- a/io-console.gemspec +++ b/io-console.gemspec @@ -39,10 +39,10 @@ Gem::Specification.new do |s| s.require_paths.unshift('jruby/lib') s.files.concat(%w[ jruby/lib/io/console.rb - jruby/lib/io/console/backend/ffi.rb + jruby/lib/io/console/backend/ffi/termios.rb + jruby/lib/io/console/backend/ffi/windows.rb jruby/lib/io/console/backend/stty.rb jruby/lib/io/console/backend/stub.rb - jruby/lib/io/console/backend/windows.rb jruby/lib/io/console/common.rb jruby/lib/io/console/constants/bsd.rb jruby/lib/io/console/constants/linux.rb diff --git a/jruby/lib/io/console.rb b/jruby/lib/io/console.rb index 724114b..b804a93 100644 --- a/jruby/lib/io/console.rb +++ b/jruby/lib/io/console.rb @@ -38,14 +38,14 @@ class Mode backends = [] # If Linux or BSD, try to load the native version -case RbConfig::CONFIG['host_os'].downcase -when /darwin|openbsd|freebsd|netbsd/ - backends << %w[ffi bsd] << %w[stty] -when /linux/ - backends << %w[ffi linux] << %w[stty] +case RbConfig::CONFIG['host_os'] +when /darwin|openbsd|freebsd|netbsd/i + backends << %w[ffi/termios bsd] << %w[stty] +when /linux/i + backends << %w[ffi/termios linux] << %w[stty] when /mswin|win32|ming/i require_relative 'console/constants/windows' - require_relative 'console/backend/windows' + require_relative 'console/backend/ffi/windows' return else backends << %w[stty] @@ -55,7 +55,7 @@ class Mode require_relative "console/constants/#{constants}" if constants require_relative "console/backend/#{backend}" rescue Exception => ex - warn "failed to load #{backend} console support: #{ex}" if $VERBOSE + warn "failed to load #{backend.split('/', 2).first} console support: #{ex}" if $VERBOSE else true end diff --git a/jruby/lib/io/console/backend/ffi.rb b/jruby/lib/io/console/backend/ffi/termios.rb similarity index 70% rename from jruby/lib/io/console/backend/ffi.rb rename to jruby/lib/io/console/backend/ffi/termios.rb index 211bbbc..151d404 100644 --- a/jruby/lib/io/console/backend/ffi.rb +++ b/jruby/lib/io/console/backend/ffi/termios.rb @@ -1,3 +1,62 @@ +require 'ffi' + +case RbConfig::CONFIG['host_os'] +when /darwin/i + tested_platforms = /i386|x86_64/ + unless tested_platforms.match?(FFI::Platform::ARCH) + raise LoadError, "native console on MacOS only supported on #{tested_platforms.source.gsub('|', ', ')}" + end +when /linux/i + tested_platforms = /i386|x86_64|powerpc64|aarch64|s390x/ + unless tested_platforms.match?(FFI::Platform::ARCH) + warn "native console only tested on #{tested_platforms.source.gsub('|', ', ')}" + end +end + +module IO::Console::LibC + include IO::Console::Constants + extend FFI::Library + ffi_lib FFI::Library::LIBC + + typedef TCFLAG_TYPE, :tcflag_t + typedef SPEED_TYPE, :speed_t + + class Termios < FFI::Struct + constants = IO::Console::Constants + fields = [ + :c_iflag, :tcflag_t, + :c_oflag, :tcflag_t, + :c_cflag, :tcflag_t, + :c_lflag, :tcflag_t, + ] + fields.concat([:c_line, :uchar]) if constants::TERMIOS_HAS_LINE + fields.concat([ + :c_cc, [:uchar, constants::NCCS], + :c_ispeed, :speed_t, + :c_ospeed, :speed_t, + ]) + layout(*fields) + end + + class Winsize < FFI::Struct + layout \ + :ws_row, :ushort, + :ws_col, :ushort, + :ws_xpixel, :ushort, + :ws_ypixel, :ushort + end + + attach_function :tcsetattr, [:int, :int, Termios], :int + attach_function :tcgetattr, [:int, Termios], :int + attach_function :cfgetispeed, [Termios], :speed_t + attach_function :cfgetospeed, [Termios], :speed_t + attach_function :cfsetispeed, [Termios, :speed_t], :int + attach_function :cfsetospeed, [Termios, :speed_t], :int + attach_function :cfmakeraw, [Termios], :int + attach_function :tcflush, [:int, :int], :int + attach_function :ioctl, [:int, :ulong, :varargs], :int +end + # Common logic that uses native calls for console module IO::Console def ttymode diff --git a/jruby/lib/io/console/backend/windows.rb b/jruby/lib/io/console/backend/ffi/windows.rb similarity index 98% rename from jruby/lib/io/console/backend/windows.rb rename to jruby/lib/io/console/backend/ffi/windows.rb index 787a5b6..79b9a47 100644 --- a/jruby/lib/io/console/backend/windows.rb +++ b/jruby/lib/io/console/backend/ffi/windows.rb @@ -1,13 +1,6 @@ require 'ffi' module IO::Console::Windows - STD_INPUT_HANDLE = -10 - STD_OUTPUT_HANDLE = -11 - WAIT_OBJECT_0 = 0 - WAIT_TIMEOUT = 258 - ENABLE_WRAP_AT_EOL_OUTPUT = 2 - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 - module Native extend FFI::Library ffi_convention :stdcall diff --git a/jruby/lib/io/console/constants/bsd.rb b/jruby/lib/io/console/constants/bsd.rb index 3c4c18d..2eb1064 100644 --- a/jruby/lib/io/console/constants/bsd.rb +++ b/jruby/lib/io/console/constants/bsd.rb @@ -1,22 +1,12 @@ -require 'ffi' - -tested_platforms = %w[i386 x86_64] - -if RbConfig::CONFIG['host_os'].downcase =~ /darwin/ && FFI::Platform::ARCH !~ /#{tested_platforms.join('|')}/ - raise LoadError.new("native console on MacOS only supported on #{tested_platforms.join(', ')}") -end - -module IO::Console::LibC - extend FFI::Library - ffi_lib FFI::Library::LIBC - - if RbConfig::CONFIG['host_os'].downcase =~ /darwin/ - typedef :ulong, :tcflag_t - typedef :ulong, :speed_t +module IO::Console::Constants + if /darwin/i.match?(RbConfig::CONFIG['host_os']) + TCFLAG_TYPE = :ulong + SPEED_TYPE = :ulong else - typedef :uint, :tcflag_t - typedef :uint, :speed_t + TCFLAG_TYPE = :uint + SPEED_TYPE = :uint end + TERMIOS_HAS_LINE = false # Special Control Characters VEOF = 0 # ICANON @@ -124,49 +114,6 @@ module IO::Console::LibC IOCPARM_MASK = 0x1fff IOC_OUT = 0x40000000 IOC_IN = 0x80000000 - - def self._IOC(inout,group,num,len) - inout | ((len & IOCPARM_MASK) << 16) | ((group.ord << 8) | num) - end - - def self._IOR(g,n,t) - self._IOC(IOC_OUT, g, n, find_type(t).size) - end - - def self._IOW(g,n,t) - self._IOC(IOC_IN, g, n, find_type(t).size) - end - - - class Termios < FFI::Struct - layout \ - :c_iflag, :tcflag_t, - :c_oflag, :tcflag_t, - :c_cflag, :tcflag_t, - :c_lflag, :tcflag_t, - :c_cc, [ :uchar, NCCS ], - :c_ispeed, :speed_t, - :c_ospeed, :speed_t - end - - class Winsize < FFI::Struct - layout \ - :ws_row, :ushort, - :ws_col, :ushort, - :ws_xpixel, :ushort, - :ws_ypixel, :ushort - end - - TIOCGWINSZ = _IOR('t', 104, Winsize) # get window size - TIOCSWINSZ = _IOW('t', 103, Winsize) # set window size - - attach_function :tcsetattr, [ :int, :int, Termios ], :int - attach_function :tcgetattr, [ :int, Termios ], :int - attach_function :cfgetispeed, [ Termios ], :speed_t - attach_function :cfgetospeed, [ Termios ], :speed_t - attach_function :cfsetispeed, [ Termios, :speed_t ], :int - attach_function :cfsetospeed, [ Termios, :speed_t ], :int - attach_function :cfmakeraw, [ Termios ], :int - attach_function :tcflush, [ :int, :int ], :int - attach_function :ioctl, [ :int, :ulong, :varargs ], :int + TIOCGWINSZ = 0x40087468 + TIOCSWINSZ = 0x80087467 end diff --git a/jruby/lib/io/console/constants/linux.rb b/jruby/lib/io/console/constants/linux.rb index 4df4574..b2930cd 100644 --- a/jruby/lib/io/console/constants/linux.rb +++ b/jruby/lib/io/console/constants/linux.rb @@ -1,17 +1,7 @@ -require 'ffi' - -tested_platforms = %w[i386 x86_64 powerpc64 aarch64 s390x] - -unless FFI::Platform::ARCH =~ /#{tested_platforms.join('|')}/ - warn "native console only tested on #{tested_platforms.join(', ')}" -end - -module IO::Console::LibC - extend FFI::Library - ffi_lib FFI::Library::LIBC - - typedef :uint, :tcflag_t - typedef :uint, :speed_t +module IO::Console::Constants + TCFLAG_TYPE = :uint + SPEED_TYPE = :uint + TERMIOS_HAS_LINE = true VINTR = 0 VQUIT = 1 @@ -168,37 +158,6 @@ module IO::Console::LibC TCSADRAIN = 1 TCSAFLUSH = 2 NCCS = 32 - class Termios < FFI::Struct - layout \ - :c_iflag, :tcflag_t, - :c_oflag, :tcflag_t, - :c_cflag, :tcflag_t, - :c_lflag, :tcflag_t, - :c_line, :uchar, - :c_cc, [ :uchar, NCCS ], - :c_ispeed, :speed_t, - :c_ospeed, :speed_t - end - - class Winsize < FFI::Struct - layout \ - :ws_row, :ushort, - :ws_col, :ushort, - :ws_xpixel, :ushort, - :ws_ypixel, :ushort - end - - TIOCGWINSZ = 0x5413 TIOCSWINSZ = 0x5414 - - attach_function :tcsetattr, [ :int, :int, Termios ], :int - attach_function :tcgetattr, [ :int, Termios ], :int - attach_function :cfgetispeed, [ Termios ], :speed_t - attach_function :cfgetospeed, [ Termios ], :speed_t - attach_function :cfsetispeed, [ Termios, :speed_t ], :int - attach_function :cfsetospeed, [ Termios, :speed_t ], :int - attach_function :cfmakeraw, [ Termios ], :int - attach_function :tcflush, [ :int, :int ], :int - attach_function :ioctl, [ :int, :ulong, :varargs ], :int end diff --git a/jruby/lib/io/console/constants/windows.rb b/jruby/lib/io/console/constants/windows.rb index d5813e2..a0220a7 100644 --- a/jruby/lib/io/console/constants/windows.rb +++ b/jruby/lib/io/console/constants/windows.rb @@ -1,4 +1,11 @@ module IO::Console::Windows + STD_INPUT_HANDLE = -10 + STD_OUTPUT_HANDLE = -11 + WAIT_OBJECT_0 = 0 + WAIT_TIMEOUT = 258 + ENABLE_WRAP_AT_EOL_OUTPUT = 2 + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 + VK_TAB = 0x09 VK_RETURN = 0x0d VK_SHIFT = 0x10 From 48942c2803258eb32b231d3aa8a0a48b5d40cd61 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Aug 2026 17:47:17 +0900 Subject: [PATCH 4/4] Support JRuby FFI console on arm64 Darwin Use Darwin terminal ioctls because JRuby FFI corrupts terminal state when calling `tcgetattr` and `tcsetattr` on arm64. --- jruby/lib/io/console/backend/ffi/termios.rb | 32 ++++++++++++++------- jruby/lib/io/console/constants/bsd.rb | 2 ++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/jruby/lib/io/console/backend/ffi/termios.rb b/jruby/lib/io/console/backend/ffi/termios.rb index 151d404..55f55f7 100644 --- a/jruby/lib/io/console/backend/ffi/termios.rb +++ b/jruby/lib/io/console/backend/ffi/termios.rb @@ -2,7 +2,7 @@ case RbConfig::CONFIG['host_os'] when /darwin/i - tested_platforms = /i386|x86_64/ + tested_platforms = /i386|x86_64|aarch64/ unless tested_platforms.match?(FFI::Platform::ARCH) raise LoadError, "native console on MacOS only supported on #{tested_platforms.source.gsub('|', ', ')}" end @@ -46,13 +46,13 @@ class Winsize < FFI::Struct :ws_ypixel, :ushort end - attach_function :tcsetattr, [:int, :int, Termios], :int - attach_function :tcgetattr, [:int, Termios], :int - attach_function :cfgetispeed, [Termios], :speed_t - attach_function :cfgetospeed, [Termios], :speed_t - attach_function :cfsetispeed, [Termios, :speed_t], :int - attach_function :cfsetospeed, [Termios, :speed_t], :int - attach_function :cfmakeraw, [Termios], :int + attach_function :tcsetattr, [:int, :int, :pointer], :int + attach_function :tcgetattr, [:int, :pointer], :int + attach_function :cfgetispeed, [:pointer], :speed_t + attach_function :cfgetospeed, [:pointer], :speed_t + attach_function :cfsetispeed, [:pointer, :speed_t], :int + attach_function :cfsetospeed, [:pointer, :speed_t], :int + attach_function :cfmakeraw, [:pointer], :void attach_function :tcflush, [:int, :int], :int attach_function :ioctl, [:int, :ulong, :varargs], :int end @@ -61,7 +61,12 @@ class Winsize < FFI::Struct module IO::Console def ttymode termios = LibC::Termios.new - if LibC.tcgetattr(self.fileno, termios) != 0 + result = if LibC.const_defined?(:TIOCGETA) + LibC.ioctl(fileno, LibC::TIOCGETA, :pointer, termios.pointer) + else + LibC.tcgetattr(fileno, termios.pointer) + end + if result != 0 raise SystemCallError.new(respond_to?(:path) ? path : "tcgetattr", FFI.errno) end @@ -74,7 +79,12 @@ def ttymode private :ttymode def ttymode_set(termios) - if LibC.tcsetattr(self.fileno, LibC::TCSANOW, termios) != 0 + result = if LibC.const_defined?(:TIOCSETA) + LibC.ioctl(fileno, LibC::TIOCSETA, :pointer, termios.pointer) + else + LibC.tcsetattr(fileno, LibC::TCSANOW, termios.pointer) + end + if result != 0 raise SystemCallError.new(respond_to?(:path) ? path : "tcsetattr(TCSANOW)", FFI.errno) end end @@ -93,7 +103,7 @@ def ttymode_yield(block, **opts, &setup) private :ttymode_yield TTY_RAW = Proc.new do |t, min: 1, time: nil, intr: nil| - LibC.cfmakeraw(t) + LibC.cfmakeraw(t.pointer) t[:c_lflag] &= ~(LibC::ECHOE|LibC::ECHOK) if min >= 0 t[:c_cc][LibC::VMIN] = min diff --git a/jruby/lib/io/console/constants/bsd.rb b/jruby/lib/io/console/constants/bsd.rb index 2eb1064..2760581 100644 --- a/jruby/lib/io/console/constants/bsd.rb +++ b/jruby/lib/io/console/constants/bsd.rb @@ -2,6 +2,8 @@ module IO::Console::Constants if /darwin/i.match?(RbConfig::CONFIG['host_os']) TCFLAG_TYPE = :ulong SPEED_TYPE = :ulong + TIOCGETA = 0x40487413 + TIOCSETA = 0x80487414 else TCFLAG_TYPE = :uint SPEED_TYPE = :uint