[前][次][番号順一覧][スレッド一覧]

ruby-changes:66520

From: aycabta <ko1@a...>
Date: Mon, 21 Jun 2021 18:44:34 +0900 (JST)
Subject: [ruby-changes:66520] c59bbd86a6 (master): [ruby/reline] Use ReadConsoleInputW() instead of getwch()

https://git.ruby-lang.org/ruby.git/commit/?id=c59bbd86a6

From c59bbd86a6bd0f1ce8a7babf56feabeb41a7e675 Mon Sep 17 00:00:00 2001
From: aycabta <aycabta@g...>
Date: Fri, 16 Apr 2021 19:59:36 +0900
Subject: [ruby/reline] Use ReadConsoleInputW() instead of getwch()

This needs https://github.com/aycabta/yamatanooroti/pull/19, which is released
by yamatanooroti gem 0.0.7, to test with yamatanooroti.

https://github.com/ruby/reline/commit/06c1f45da1
---
 lib/reline/windows.rb                       | 136 +++++++++++++++-------------
 test/reline/yamatanooroti/test_rendering.rb |  23 +++++
 2 files changed, 97 insertions(+), 62 deletions(-)

diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb
index c645458..50cfe3f 100644
--- a/lib/reline/windows.rb
+++ b/lib/reline/windows.rb
@@ -89,9 +89,30 @@ class Reline::Windows https://github.com/ruby/ruby/blob/trunk/lib/reline/windows.rb#L89
   VK_LMENU = 0xA4
   VK_CONTROL = 0x11
   VK_SHIFT = 0x10
+
+  KEY_EVENT = 0x01
+  WINDOW_BUFFER_SIZE_EVENT = 0x04
+
+  CAPSLOCK_ON = 0x0080
+  ENHANCED_KEY = 0x0100
+  LEFT_ALT_PRESSED = 0x0002
+  LEFT_CTRL_PRESSED = 0x0008
+  NUMLOCK_ON = 0x0020
+  RIGHT_ALT_PRESSED = 0x0001
+  RIGHT_CTRL_PRESSED = 0x0004
+  SCROLLLOCK_ON = 0x0040
+  SHIFT_PRESSED = 0x0010
+
+  VK_END = 0x23
+  VK_HOME = 0x24
+  VK_LEFT = 0x25
+  VK_UP = 0x26
+  VK_RIGHT = 0x27
+  VK_DOWN = 0x28
+  VK_DELETE = 0x2E
+
   STD_INPUT_HANDLE = -10
   STD_OUTPUT_HANDLE = -11
-  WINDOW_BUFFER_SIZE_EVENT = 0x04
   FILE_TYPE_PIPE = 0x0003
   FILE_NAME_INFO = 2
   @@getwch = Win32API.new('msvcrt', '_getwch', [], 'I')
@@ -105,7 +126,7 @@ class Reline::Windows https://github.com/ruby/ruby/blob/trunk/lib/reline/windows.rb#L126
   @@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE)
   @@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE)
   @@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L')
-  @@ReadConsoleInput = Win32API.new('kernel32', 'ReadConsoleInput', ['L', 'P', 'L', 'P'], 'L')
+  @@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L')
   @@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L')
   @@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I')
   @@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L')
@@ -157,78 +178,69 @@ class Reline::Windows https://github.com/ruby/ruby/blob/trunk/lib/reline/windows.rb#L178
     name =~ /(msys-|cygwin-).*-pty/ ? true : false
   end
 
-  def self.getwch
-    unless @@input_buf.empty?
-      return @@input_buf.shift
-    end
-    while @@kbhit.call == 0
-      sleep(0.001)
-    end
-    until @@kbhit.call == 0
-      ret = @@getwch.call
-      if ret == 0 or ret == 0xE0
-        @@input_buf << ret
-        ret = @@getwch.call
-        @@input_buf << ret
-        return @@input_buf.shift
-      end
-      begin
-        bytes = ret.chr(Encoding::UTF_8).bytes
-        @@input_buf.push(*bytes)
-      rescue Encoding::UndefinedConversionError
-        @@input_buf << ret
-        @@input_buf << @@getwch.call if ret == 224
+  def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
+    char = char_code.chr(Encoding::UTF_8)
+    if char_code == 0x0D and control_key_state.anybits?(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED | SHIFT_PRESSED)
+      # It's treated as Meta+Enter on Windows.
+      @@output_buf.push("\e".ord)
+      @@output_buf.push(char_code)
+    elsif control_key_state.anybits?(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
+      @@output_buf.push("\e".ord)
+      @@output_buf.concat(char.bytes)
+    elsif control_key_state.anybits?(ENHANCED_KEY)
+      case virtual_key_code # Emulate getwch() key sequences.
+      when VK_END
+        @@output_buf.push(0, 79)
+      when VK_HOME
+        @@output_buf.push(0, 71)
+      when VK_LEFT
+        @@output_buf.push(0, 75)
+      when VK_UP
+        @@output_buf.push(0, 72)
+      when VK_RIGHT
+        @@output_buf.push(0, 77)
+      when VK_DOWN
+        @@output_buf.push(0, 80)
+      when VK_DELETE
+        @@output_buf.push(0, 83)
       end
+    elsif char_code == 0 and control_key_state != 0
+      # unknown
+    else
+      @@output_buf.concat(char.bytes)
     end
-    @@input_buf.shift
   end
 
-  def self.getc
+  def self.check_input_event
     num_of_events = 0.chr * 8
-    while @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) != 0 and num_of_events.unpack('L').first > 0
+    while @@output_buf.empty? #or true
+      next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack('L').first == 0
       input_record = 0.chr * 18
       read_event = 0.chr * 4
-      if @@ReadConsoleInput.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
+      if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_record, 1, read_event) != 0
         event = input_record[0, 2].unpack('s*').first
-        if event == WINDOW_BUFFER_SIZE_EVENT
+        case event
+        when WINDOW_BUFFER_SIZE_EVENT
           @@winch_handler.()
+        when KEY_EVENT
+          key_down = input_record[4, 4].unpack('l*').first
+          repeat_count = input_record[8, 2].unpack('s*').first
+          virtual_key_code = input_record[10, 2].unpack('s*').first
+          virtual_scan_code = input_record[12, 2].unpack('s*').first
+          char_code = input_record[14, 2].unpack('S*').first
+          control_key_state = input_record[16, 2].unpack('S*').first
+          is_key_down = key_down.zero? ? false : true
+          if is_key_down
+            process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state)
+          end
         end
       end
     end
-    unless @@output_buf.empty?
-      return @@output_buf.shift
-    end
-    input = getwch
-    meta = (@@GetKeyState.call(VK_LMENU) & 0x80) != 0
-    control = (@@GetKeyState.call(VK_CONTROL) & 0x80) != 0
-    shift = (@@GetKeyState.call(VK_SHIFT) & 0x80) != 0
-    force_enter = !input.instance_of?(Array) && (control or shift) && input == 0x0D
-    if force_enter
-      # It's treated as Meta+Enter on Windows
-      @@output_buf.push("\e".ord)
-      @@output_buf.push(input)
-    else
-      case input
-      when 0x00
-        meta = false
-        @@output_buf.push(input)
-        input = getwch
-        @@output_buf.push(*input)
-      when 0xE0
-        @@output_buf.push(input)
-        input = getwch
-        @@output_buf.push(*input)
-      when 0x03
-        @@output_buf.push(input)
-      else
-        @@output_buf.push(input)
-      end
-    end
-    if meta
-      "\e".ord
-    else
-      @@output_buf.shift
-    end
+  end
+
+  def self.getc
+    check_input_event
+    @@output_buf.shift
   end
 
   def self.ungetc(c)
diff --git a/test/reline/yamatanooroti/test_rendering.rb b/test/reline/yamatanooroti/test_rendering.rb
index 13693e7..f877916 100644
--- a/test/reline/yamatanooroti/test_rendering.rb
+++ b/test/reline/yamatanooroti/test_rendering.rb
@@ -730,6 +730,29 @@ begin https://github.com/ruby/ruby/blob/trunk/test/reline/yamatanooroti/test_rendering.rb#L730
       EOC
     end
 
+    def test_meta_key
+      start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+      write("def ge\M-bho")
+      close
+      assert_screen(<<~EOC)
+        Multiline REPL.
+        prompt> def hoge
+      EOC
+    end
+
+    def test_force_enter
+      start_terminal(50, 200, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl}, startup_message: 'Multiline REPL.')
+      write("def hoge\nend\C-p\C-e")
+      write("\M-\x0D")
+      close
+      assert_screen(<<~EOC)
+        Multiline REPL.
+        prompt> def hoge
+        prompt>
+        prompt> end
+      EOC
+    end
+
     private def write_inputrc(content)
       File.open(@inputrc_file, 'w') do |f|
         f.write content
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]