From 1eb748730e815a1cd168bd1f59265116de129717 Mon Sep 17 00:00:00 2001 From: YO4 Date: Tue, 6 Jan 2026 21:22:24 +0900 Subject: [PATCH 1/8] improve startmenu.rb irb with the RubyInstaller2 logo and help command guidance. also clear the screen when it seems appropriate. --- lib/ruby_installer/runtime/console_ui.rb | 4 ++++ resources/files/startmenu.rb | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/ruby_installer/runtime/console_ui.rb b/lib/ruby_installer/runtime/console_ui.rb index be71fe7b..b972a295 100644 --- a/lib/ruby_installer/runtime/console_ui.rb +++ b/lib/ruby_installer/runtime/console_ui.rb @@ -189,6 +189,10 @@ def initialize end end + def clear_screen + IO.console.write "\e[H" "\e[2J" + end + def set_consolemode @hConsoleHandle = @GetStdHandle.call(STD_INPUT_HANDLE) @base_console_input_mode = getconsolemode diff --git a/resources/files/startmenu.rb b/resources/files/startmenu.rb index cfb0d6a7..86d25603 100644 --- a/resources/files/startmenu.rb +++ b/resources/files/startmenu.rb @@ -11,7 +11,9 @@ Ruby EOT bm.add_button bt do - puts "\nStarting irb" + app.clear_screen + RubyInstaller::Runtime::Ridk.print_logo + puts "\nStarting irb. To show the irb command help, type `help` and press Enter." Kernel.exec File.join(RbConfig::CONFIG["bindir"], "irb.bat") end @@ -33,6 +35,7 @@ server EOT bm.add_button bt do + app.clear_screen puts "\nShow documentation of local installed gems" gem = File.join(RbConfig::CONFIG["bindir"], "gem") system gem, "install", "--conservative", "webrick", "rubygems-server" @@ -46,6 +49,7 @@ command line EOT bm.add_button bt do + app.clear_screen puts "\nRun cmd.exe with ruby environment variables (ridk enable)" ridk = File.join(RbConfig::CONFIG["bindir"], "ridk") Kernel.exec "cmd", "/E:ON", "/K", ridk, "enable" From 4e72c15fc9656b7fdede429744d8b8286aa5dbad Mon Sep 17 00:00:00 2001 From: YO4 Date: Tue, 6 Jan 2026 21:22:58 +0900 Subject: [PATCH 2/8] .cmd to test startmenu.rb --- bin/startmenu.cmd | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 bin/startmenu.cmd diff --git a/bin/startmenu.cmd b/bin/startmenu.cmd new file mode 100644 index 00000000..8e944f98 --- /dev/null +++ b/bin/startmenu.cmd @@ -0,0 +1,4 @@ +@echo off + +set dir=%~dp0 +ruby -r "%dir:\=/%../lib/ruby_installer/runtime/console_ui" "%dir:\=/%../resources/files/startmenu.rb" From b2601e731a126ee901988c40de223859d468b3f5 Mon Sep 17 00:00:00 2001 From: YO4 Date: Tue, 6 Jan 2026 22:40:16 +0900 Subject: [PATCH 3/8] robust console read When selecting while a large amount of console input is arriving at high rate, the console read thread may continue reading. For example, hold down the cursor keys to move the mouse cursor and click while doing so. To address this, only perform console input when necessary. --- lib/ruby_installer/runtime/console_ui.rb | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/ruby_installer/runtime/console_ui.rb b/lib/ruby_installer/runtime/console_ui.rb index b972a295..3b69aaa8 100644 --- a/lib/ruby_installer/runtime/console_ui.rb +++ b/lib/ruby_installer/runtime/console_ui.rb @@ -177,7 +177,9 @@ def initialize @GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L') @SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L') + @hConsoleHandle = @GetStdHandle.call(STD_INPUT_HANDLE) @ev_r, @ev_w = IO.pipe.map(&:binmode) + @read_request_queue = Thread::Queue.new set_consolemode @@ -194,14 +196,15 @@ def clear_screen end def set_consolemode - @hConsoleHandle = @GetStdHandle.call(STD_INPUT_HANDLE) @base_console_input_mode = getconsolemode setconsolemode(ENABLE_PROCESSED_INPUT | ENABLE_MOUSE_INPUT | ENABLE_EXTENDED_FLAGS | ENABLE_VIRTUAL_TERMINAL_INPUT) end def unset_consolemode if @base_console_input_mode +IO.console.write "." setconsolemode(@base_console_input_mode | ENABLE_EXTENDED_FLAGS) +IO.console.write "+" @base_console_input_mode = nil if block_given? begin @@ -257,6 +260,7 @@ def unset_consolemode private def register_stdin Thread.new do str = +"" + @read_request_queue.shift c = IO.console while char=c.read(1) str << char @@ -264,14 +268,19 @@ def unset_consolemode str == "\e" || str == "\e[" || str == "\xE0" || - str.match(/\A\e\x5b<[^Mm]*\z/) + str.match(/\A\e\x5b<[0-9;]*\z/) @ev_w.write [2, str.size, str].pack("CCa*") str = +"" + @read_request_queue.shift end end end + private def request_read + @read_request_queue.push true + end + private def handle_key_input(str) case str when "\e[D", "\xE0K".b # cursor left @@ -293,12 +302,15 @@ def unset_consolemode widget.select end end + when /\e\x5b<\d+;(\d+);(\d+)[Mm]/ # other mouse events + return # no repaint end widget.repaint end private def main_loop str = +"" + request_read while char=@ev_r.read(1) case char when "\x01" @@ -308,10 +320,10 @@ def unset_consolemode str = @ev_r.read(strlen) handle_key_input(str) - widget.repaint else raise "unexpected event: #{char.inspect}" end + request_read end end @@ -332,15 +344,19 @@ def run! end bm.add_button "text2" do p :button_2 + exit end bm.add_button "text3\nabc\noollla\n text3\nabc\noollla" do p :button_3 + exit end bm.add_button "text4\ndef" do p :button_4 + exit end bm.add_button "text5\nabc" do p :button_5 + exit end app.widget = bm app.run! From 902be548586e8cb2bf51b647b7803d00ab6fe3ab Mon Sep 17 00:00:00 2001 From: YO4 Date: Tue, 6 Jan 2026 22:44:07 +0900 Subject: [PATCH 4/8] improve console window size calculation reserve lines for the headline. handle cases when the console is too small. --- lib/ruby_installer/runtime/console_ui.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/ruby_installer/runtime/console_ui.rb b/lib/ruby_installer/runtime/console_ui.rb index 3b69aaa8..041f7c1d 100644 --- a/lib/ruby_installer/runtime/console_ui.rb +++ b/lib/ruby_installer/runtime/console_ui.rb @@ -113,11 +113,13 @@ def click(x, y) # Paint the boxes def repaint(width: @con.winsize[1], height: @con.winsize[0]) + headroom = headline.size > 0 ? (headline.size + width - 1) / width : 0 # roughly obw = (width.to_f / @ncols).floor spw = obw - 4 nrows = (@boxes.size.to_f / @ncols).ceil - obh = (height.to_f / nrows).floor + obh = ((height - headroom).to_f / nrows).floor sph = obh - 2 + sph = 1 if sph < 1 line = +"" slines = [[]] nrows.times do |nrow| @@ -140,6 +142,8 @@ def repaint(width: @con.winsize[1], height: @con.winsize[0]) text_line ||= "" spl = (spw - text_line.size) / 2 spr = spw - spl - text_line.size + spl = 0 if spl < 0 + spr = 0 if spr < 0 line += " #{border[3]}" + " " * spl + text_line + " " * spr + "#{border[5]} " slines.last.append(*([box_idx] * obw)) end @@ -157,7 +161,9 @@ def repaint(width: @con.winsize[1], height: @con.winsize[0]) line += "\n" slines << [] end - print "\n#{headline}\n"+line.chomp + @con.write "\e[1;1H" "\e[2J" + print "#{headline}" + print @con.cursor.last == 0 ? line.chomp : "\n#{line.chomp}" @slines = slines end end From 2d86b7c76938c2e91b97e02271278d10a57224b7 Mon Sep 17 00:00:00 2001 From: YO4 Date: Wed, 7 Jan 2026 20:41:50 +0900 Subject: [PATCH 5/8] more appropriate and smart load path Added a note regarding other files that are loaded. --- bin/startmenu.cmd | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bin/startmenu.cmd b/bin/startmenu.cmd index 8e944f98..0d0708ae 100644 --- a/bin/startmenu.cmd +++ b/bin/startmenu.cmd @@ -1,4 +1,9 @@ @echo off +REM This is sufficient to load console_ui.rb, but files located in +REM lib/ruby_installer/build cannot be loaded; instead, they are used +REM from the libraries installed with the ruby executable. +REM ie, ruby_installer/runtime/colors.rb is loaded from ruby installation. + set dir=%~dp0 -ruby -r "%dir:\=/%../lib/ruby_installer/runtime/console_ui" "%dir:\=/%../resources/files/startmenu.rb" +ruby -I "%dir:\=/%../lib" "%dir:\=/%../resources/files/startmenu.rb" From 4254a6dee018fdff82aadb2514cea50b912721e5 Mon Sep 17 00:00:00 2001 From: YO4 Date: Wed, 7 Jan 2026 20:43:13 +0900 Subject: [PATCH 6/8] remove unnecessary debug code --- lib/ruby_installer/runtime/console_ui.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ruby_installer/runtime/console_ui.rb b/lib/ruby_installer/runtime/console_ui.rb index 041f7c1d..c1dd52d1 100644 --- a/lib/ruby_installer/runtime/console_ui.rb +++ b/lib/ruby_installer/runtime/console_ui.rb @@ -208,9 +208,7 @@ def set_consolemode def unset_consolemode if @base_console_input_mode -IO.console.write "." setconsolemode(@base_console_input_mode | ENABLE_EXTENDED_FLAGS) -IO.console.write "+" @base_console_input_mode = nil if block_given? begin From eb68d85667c80207d15e6c4414bb266e40a24908 Mon Sep 17 00:00:00 2001 From: YO4 Date: Wed, 7 Jan 2026 21:10:20 +0900 Subject: [PATCH 7/8] improve startmenu headline message It seems the mouse can't be used when using conhost (not Windows Terminal). Running `start ruby startmenu.rb` from within the terminal causes identification to fail, but worrying about that is probably pointless. --- resources/files/startmenu.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/files/startmenu.rb b/resources/files/startmenu.rb index 86d25603..c4fb0b00 100644 --- a/resources/files/startmenu.rb +++ b/resources/files/startmenu.rb @@ -2,7 +2,7 @@ app = RubyInstaller::Runtime::ConsoleUi.new bm = RubyInstaller::Runtime::ConsoleUi::ButtonMatrix.new ncols: 3 -bm.headline = "Ruby startmenu - Choose item by mouse or cursor keys" +bm.headline = "Ruby startmenu - Choose item by #{ENV["WT_SESSION"] && "mouse or "}cursor keys and press Enter" bt = <<~EOT irb:> From 51b791ee3f30ddd17c60ce414788b7ef652ea597 Mon Sep 17 00:00:00 2001 From: YO4 Date: Sun, 11 Jan 2026 21:19:16 +0900 Subject: [PATCH 8/8] widget.repaint does not overuse the scroll buffer The `CSI 2 J` sequence stores screen contents in the scroll buffer in modern terminals. In this case, this is unnecessary operation. --- lib/ruby_installer/runtime/console_ui.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_installer/runtime/console_ui.rb b/lib/ruby_installer/runtime/console_ui.rb index c1dd52d1..948567a8 100644 --- a/lib/ruby_installer/runtime/console_ui.rb +++ b/lib/ruby_installer/runtime/console_ui.rb @@ -161,7 +161,7 @@ def repaint(width: @con.winsize[1], height: @con.winsize[0]) line += "\n" slines << [] end - @con.write "\e[1;1H" "\e[2J" + @con.write "\e[H" "\e[J" print "#{headline}" print @con.cursor.last == 0 ? line.chomp : "\n#{line.chomp}" @slines = slines