File: system_extensions.rb

package info (click to toggle)
ruby-highline 1.6.13-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 616 kB
  • sloc: ruby: 4,657; makefile: 2
file content (186 lines) | stat: -rwxr-xr-x 6,214 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# system_extensions.rb
#
#  Created by James Edward Gray II on 2006-06-14.
#  Copyright 2006 Gray Productions. All rights reserved.
#
#  This is Free Software.  See LICENSE and COPYING for details.

require "highline/compatibility"

class HighLine
  module SystemExtensions
    module_function

    JRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'

    #
    # This section builds character reading and terminal size functions
    # to suit the proper platform we're running on.  Be warned:  Here be
    # dragons!
    #
    begin
      # Cygwin will look like Windows, but we want to treat it like a Posix OS:
      raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i

      require "Win32API"             # See if we're on Windows.

      CHARACTER_MODE = "Win32API"    # For Debugging purposes only.

      #
      # Windows savvy getc().
      #
      # *WARNING*:  This method ignores <tt>input</tt> and reads one
      # character from +STDIN+!
      #
      def get_character( input = STDIN )
        Win32API.new("msvcrt", "_getch", [ ], "L").Call
      rescue Exception
        Win32API.new("crtdll", "_getch", [ ], "L").Call
      end

      # A Windows savvy method to fetch the console columns, and rows.
      def terminal_size
        m_GetStdHandle               = Win32API.new( 'kernel32',
                                                     'GetStdHandle',
                                                     ['L'],
                                                     'L' )
        m_GetConsoleScreenBufferInfo = Win32API.new(
          'kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L'
        )

        format        = 'SSSSSssssSS'
        buf           = ([0] * format.size).pack(format)
        stdout_handle = m_GetStdHandle.call(0xFFFFFFF5)

        m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
        _, _, _, _, _,
        left, top, right, bottom, _, _ = buf.unpack(format)
        return right - left + 1, bottom - top + 1
      end
    rescue LoadError                  # If we're not on Windows try...
      begin
        require "termios"             # Unix, first choice termios.

        CHARACTER_MODE = "termios"    # For Debugging purposes only.

        #
        # Unix savvy getc().  (First choice.)
        #
        # *WARNING*:  This method requires the "termios" library!
        #
        def get_character( input = STDIN )
          return input.getbyte if input.is_a? StringIO

          old_settings = Termios.getattr(input)

          new_settings                     =  old_settings.dup
          new_settings.c_lflag             &= ~(Termios::ECHO | Termios::ICANON)
          new_settings.c_cc[Termios::VMIN] =  1

          begin
            Termios.setattr(input, Termios::TCSANOW, new_settings)
            input.getbyte
          ensure
            Termios.setattr(input, Termios::TCSANOW, old_settings)
          end
        end
      rescue LoadError            # If our first choice fails, try using ffi-ncurses.
        begin
          require 'ffi-ncurses'   # The ffi gem is builtin to JRUBY and because stty does not
                                  # work correctly in JRuby manually installing the ffi-ncurses
                                  # gem is the only way to get highline to operate correctly in
                                  # JRuby. The ncurses library is only present on unix platforms
                                  # so this is not a solution for using highline in JRuby on
                                  # windows.

          CHARACTER_MODE = "ncurses"    # For Debugging purposes only.

          #
          # ncurses savvy getc().
          #
          def get_character( input = STDIN )
            FFI::NCurses.initscr
            FFI::NCurses.cbreak
            begin
              FFI::NCurses.curs_set 0
              input.getbyte
            ensure
              FFI::NCurses.endwin
            end
          end

        rescue LoadError                # If the ffi-ncurses choice fails, try using stty
          CHARACTER_MODE = "stty"    # For Debugging purposes only.

          #
          # Unix savvy getc().  (Second choice.)
          #
          # *WARNING*:  This method requires the external "stty" program!
          #
          def get_character( input = STDIN )
            raw_no_echo_mode

            begin
              input.getbyte
            ensure
              restore_mode
            end
          end

          #
          # Switched the input mode to raw and disables echo.
          #
          # *WARNING*:  This method requires the external "stty" program!
          #
          def raw_no_echo_mode
            @state = `stty -g`
            system "stty raw -echo -icanon isig"
          end

          #
          # Restores a previously saved input mode.
          #
          # *WARNING*:  This method requires the external "stty" program!
          #
          def restore_mode
            system "stty #{@state}"
          end
        end
      end
      if CHARACTER_MODE == 'ncurses'
        #
        # A ncurses savvy method to fetch the console columns, and rows.
        #
        def terminal_size
          size = [80, 40]
          FFI::NCurses.initscr
          begin
            size = FFI::NCurses.getmaxyx(stdscr).reverse
          ensure
            FFI::NCurses.endwin
          end
          size
        end
      elsif JRUBY
        # JRuby running on Unix can fetch the number of columns and rows from the builtin Jline library
        require 'java'
        java_import 'jline.Terminal'
        def terminal_size
          java_terminal = @java_terminal || Terminal.getTerminal
          [ java_terminal.getTerminalWidth, java_terminal.getTerminalHeight ]
        end
      else
        # A Unix savvy method using stty that to fetch the console columns, and rows.
        # ... stty does not work in JRuby
        def terminal_size
          if /solaris/ =~ RUBY_PLATFORM and
            `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
            [$2, $1].map { |c| x.to_i }
          else
            `stty size`.split.map { |x| x.to_i }.reverse
          end
        end
      end
    end
  end
end