Crystal: How to get Terminal width?

Created on 24 Jan 2016  Â·  18Comments  Â·  Source: crystal-lang/crystal

In Ruby, I can use the https://github.com/akr/ruby-terminfo gem to get the character width of my terminal. How can I do this in Crystal?

Most helpful comment

I was finally able to get these values with termbox-crystal which wraps the Termbox C library. You can use the whole library, or just grab the width and height and continue on your own, as I did.

All 18 comments

I think that should be provided by an external library. I don't know if there is one, try searching here.

Sadly, there doesn't appear to be one. I guess I'll have a crack at implementing it myself.

@stugol I don't think you need a library for this. You already can do it. You can access this information through the environment variables:

# print width of the current terminal window
puts ENV["COLUMNS"]  # => 142

#  the same, but for hight
puts ENV["LINES"]    # => 38

I guess, the issue can be closed)

No, that doesn't always work. Sometimes the environment variables are not set, and you have to get it some other way.

Ruby is battle tested and there are gems for everything. Crystal is young, and there aren't that many shards, yet. You'll have to roll up your sleeves!

Please open this issue again. The problem cannot be solved by writing Crystal code, as the "magic numbers" needed for the ioctl() call are platform-specific. It is impossible to write a third-party Crystal library to implement this.

@stugol I wonder, why is it impossible? If I understand you correctly, here are plenty of examples of how to write platform-dependent code: https://github.com/manastech/crystal/search?utf8=%E2%9C%93&q=ifdef - good old ifdef.

For example, the code here works perfectly on my Debian system:

module I::Terminal
    lib C
        struct Winsize
            ws_row : UInt16         # rows, in characters */
            ws_col : UInt16         # columns, in characters */
            ws_xpixel : UInt16  # horizontal size, pixels
            ws_ypixel : UInt16  # vertical size, pixels
        end
        fun ioctl(fd : Int32, request : UInt32, winsize : C::Winsize*) : Int32
    end

    def self.get_terminal_size()
        C.ioctl(0, 21523, out screen_size)      # magic number
        screen_size
    end
end
puts I::Terminal.get_terminal_size.ws_col

But on your system, it may give nonsense values. Try it here for instance. See? Utter madness!

@waterlink: It is impossible because it can be different on different systems, even if they're all 64-bit or all running Linux!

According to stackoverflow: "[TIOCGWINSZ] is a magic constant determined by the system you are running on [...] by the terminal driver."

So it can't be relied upon to remain constant between systems! You need the platform headers specific to that system. And when can a Crystal user be relied upon to encounter the platform headers? Right! When he compiles Crystal. So the constant needs to be defined in the Crystal source somewhere.

@stugol Do I understand correctly, that values will change depending on the terminal you are running?

It seems like one can achieve that by either:

  • write small C library (with sane function, that does not accept any magic constants) and call it from crystal + provide a way to build this beast properly (crystal + C code); or
  • make *.h => *.cr auto-conversion possible and do it during build process. (I have a feeling, that won't work, since values will have to be different at runtime..).

For instance, Ruby itself, doesn't allow you to do it with standard library, you will have to bring in some dependency (like highline, for example). And all of these libraries, you can bring in to get terminal width, use C extension to do that.

Aforementioned library ruby-terminfo does exactly that: https://github.com/akr/ruby-terminfo/blob/master/terminfo.c

So, I suppose, the answer to the original question: do the same thing ruby-terminfo does, write some C code and bind to it.

We have plans to integrate crystal_lib into the language, so that this can be done: you tell the compiler you want to use a constant from a give header, and this is determined at compile time. That way there's no need to use ifdef and the correct value is picked up according to the header for the current OS. The same goes for structs and functions.

But, like everything else, it will take time, and is not the top-priority at the moment.

@waterlink: It is normal for a gem to compile itself when you install it. The gem command exists for this purpose. If Crystal has such a system, I am unaware of it.

Oh I was unaware that shards can do that! That is very good! Thanks!

Von meinem iPhone gesendet

Am 25.01.2016 um 8:17 AM schrieb Julien Portalier [email protected]:

A quick search would have revealed:
https://github.com/ysbaddaden/shards/blob/master/SPEC.md#scripts

And a full blown example:
ysbaddaden/shards#73 (comment)

—
Reply to this email directly or view it on GitHub.

I was finally able to get these values with termbox-crystal which wraps the Termbox C library. You can use the whole library, or just grab the width and height and continue on your own, as I did.

Note of caution: including / using Termbox seems to change terminal modes, such that arrow keys are now received as different bytes. I'm guessing this has to do with all the different possible flags for the terminal mode, but it was extremely confusing to me when my main program and my tiny keyboard input demo program were receiving different bytes / chars from pressing the arrow keys.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oprypin picture oprypin  Â·  3Comments

pbrusco picture pbrusco  Â·  3Comments

will picture will  Â·  3Comments

lbguilherme picture lbguilherme  Â·  3Comments

nabeelomer picture nabeelomer  Â·  3Comments