Skip to content
Cecil edited this page Dec 20, 2016 · 1 revision

It's a little hard to understand the need for this without an explanation. Shoes has a log window that you can write to with debug/info/error commands and Ruby exceptions display there. It's all nice and pretty. Except it's not really a console.

Issue #17 rightfully complains that it's hard to see C printf or ruby puts on OSX.

Fixing issue #110 introduced a --console command line arg for packaged Windows apps that creates a new DOS terminal all hooked to stdin/stdout which is pretty damn cool -- see bin/main.skel and AllocConsole();

Issue #124 hopes to implement the command line version of byebug but byebug suspends Ruby threads and of course the main Ruby thread is also the GTK thread on Windows and Linux.

In Shoes, there may not be anything hooked up to stdin/stdout, depending on how it was started. Starting Shoes from the Programming->Shoes federales menu -- nothing is connected for stdin/stdout/stderr. Windows too unless you do the --console thing for a packaged script. Shoes on OSX is the same. On OSX, even if you start Shoes from a command line there may be nothing connected to stdin/stdout/stderr.

But we want a console for the loose puts and printfs that I and others leave in our code. A pop up screen you can type into (that you can read from stdin) and a scrolling terminal to display (stdout/stderr) just like a terminal (console)

The Windows code exists and works OK. Linux and OSX - We need a terminal emulator written in Gtk-C and another one written in Cocoa/Obj-C. Once the console window is up and hooked into stdio/stdout, Shoes and Ruby won't even know it exists.

tesi is a very old project but it still compiles on Linux with a change or two and still almost works and it's damn small - we're not reinventing gnome-terminal. It doesn't scroll properly, the window is too large, the font is wrong, it tries too hard to emulate vt102 scroll regions and so on. Even worse it forks(/bin/bash) which is exactly want Shoes does not want to do - create a new process. On the plus side, it uses a pty to manage the terminal side which should make the Linux and OSX code we need in Shoes a lot easier and it's bare bones. No menus or tabbed interfaces or font selectors or .ini files and we don't have to care about incorrect escape handling just to get a dumb terminal on the screen for Shoes and hooked into stdin/stdout for things like irb or some other console oriented program.

[2016-06-25] Shoes 3.2.24 will probably have this feature. For now, it's only available if you build from source on the console branch for Linux and Windows. You can bring the console up by using --console on the starting command line note: cshoes.exe --console will not create a new console window - it already has one. shoes.exe --console does create a new console. On Linux, dist/shoes --console samples/simple-info.rb for example will bring up a new console window.

You can also bring up the console from a Shoes script:

Shoes.app do
  Shoes::show_console
  para "#{Time.now}\n"    
  para "#{Time.local(2015, 06, 19)}\n"
  $stderr.puts "do you see this?"
end

The Shoes::show_console method not only brings up a new Window but also redirects stdin/stdout/stderr to it. Keypress in that window can be read by by the Shoes script by reading STDIN. linux-console

That example shoes that the Linux console responds to sending it a tab stop (8 chars wide) or a backspace (which you can't see, but trust me, it's in the created message sent to the console.

Cross platform issues

On Linux and probably OSX, the console is exceedingly dumb about escape sequences, both sending and receiving". On Windows the integration is better but if you send escape sequences to set colors and Windows sh*t' it won't work well for anyone else that runs that script, even on other Windows. Don't don't do it. Please?

Don't depend on tab stops working either, cross platform. Or having arrow keys. You get backspace.

Console Input

If you want to read typed characters or lines from the console window then you have to accept many limitations. You should use readline (the gem is included with Shoes 3.2.23+) and just not worry about characters. Of course some of you won't. I suggest you study this (it may in samples/experts-console.rb in 3.24+)

 def getln_raw () 
    ln = ""
    begin
    loop do
      ch = $stdin.getch
      if ch == ?\b # backspace
        len = ln.length
        if len > 0
          $stdout.write "\b \b"
          ln.chop!
        end
      elsif ch == ?\r
        $stdout.write("\r\n")
        return ln
      else 
        $stdout.putc ch
        ln += ch
      end
    end
    rescue => e
      $stdout.puts e.inspect
    end
 end

Shoes.app do
  stack do
    para "This will show the console and run a loop that echos keyboard \
input until you enter 'quit' at the beginning of a line. quit will exit Shoes \n"
    para "Select your input method"
    flow { @r1 = radio :io; para "io/console raw"}
    flow { @r2 = radio :io; para "io/console cooked"}
    flow { @r3 = radio :io; para "readline"}
    button "run loop" do
      Shoes::show_console
      if @r1.checked?
        require 'io/console'
        STDIN.raw!
        Thread.new do
          $stdout.write "> "
          loop do
            ln = getln_raw
            if ln.strip == 'quit' 
              $stderr.write "really quit (y/n)"
              ans = getln_raw.strip
              exit if ans == 'y'
            end
            $stdout.puts "IR: #{ln}"
          end
        end
      elsif @r2.checked?
        require 'io/console'
        STDIN.cooked!
        Thread.new do
          $stdout.write "> "
          loop do
            ln = STDIN.cooked(&:gets)
            if ln.strip == 'quit' 
              $stderr.write "really quit (y/n)"
              ans = $stdin.gets.strip
              exit if ans == 'y'
            end
            $stdout.puts "IC: #{ln}"
          end
        end
      elsif @r3.checked?
        require 'readline'
        Thread.new do
          loop do
            ln = Readline.readline('> ', false)
            if ln.strip == 'quit' 
              $stderr.write "really quit (y/n)"
              ans = Readline.readline.strip
              exit if ans == 'y'
            end
            $stdout.puts "RL: #{ln}"
          end
        end
      end
    end
  end
end

Yes, you do want/need a new Thread and you probably shouldn't issue any Shoes GUI altering stuff inside it, please? Notice the horse sh*t needed to handle backspace in 'raw' mode. Don't be that person - use readline. Of course you'll notice the difference between puts and write and how STDIN is the same as $stdin and stdout and stderr in all their incarnations are the same place.

This is not the place to read keystrokes to implement a game.Just don't do that. Your users will hate you and I'll mock you, publicly for ignoring this advice and warning.

In fact, on Windows, only the readline code above works. Don't expect a fix for that. readline is all you get.

Clone this wiki locally