How to use Raart to draw in terminal?

266 views
Skip to first unread message

Evžen Wybitul

unread,
Jun 19, 2018, 4:16:31 AM6/19/18
to Racket Users
Hello, I'm not sure the title is 100% correct; I'm looking for a library similar to libtcod or curses, which abstracts above a terminal and allows one to say something like draw a red "^"  on position (8 7). I've already done some search and quite a few libraries turned out. Each has a big disadvantage (I list them below) though, so I'm asking here whether there is some library I missed or how can be the specific disadvantage mitigated.

1) Raart — I have no idea how that works. There is no documentation and the tests are sparse and don't help me much (as I'm a relative beginner).
2) CharTerm — doesn't support colors and unicode, AFAIK.
3) 2htdp/universe — relatively slow, doesn't really emulate terminal, but draw tiles on a rectangle.
4) pltcod (libtcod wrapper) — is a wrapper for an old version, setup is complicated, libtcod is too big and does too many things.

The best case would be making 1) work. Is there anybody who worked with raart and could point me in the right direction?

Alex Harsanyi

unread,
Jun 19, 2018, 6:58:15 AM6/19/18
to Racket Users
Why not roll your own using ANSI escape sequences from https://en.wikipedia.org/wiki/ANSI_escape_code:

    #lang racket
    (define (clear-display) (printf "\e[2J"))
    (define (move-cursor-to x y) (printf "\e[~a;~aH" x y))
    (define (save-cursor) (printf "\e[s"))
    (define (restore-cursor) (printf "\e[u"))
    (define (text-color c) (printf "\e[~am" c))
    (define (restore-text-color) (printf "\e[39m"))

    (define red 31)
    (define green 32)
    (define yellow 33)
    (define blue 34)

    (save-cursor)
    (clear-display)
    (move-cursor-to 10 10)
    (text-color red)
    (printf "Hello")
    (move-cursor-to 15 15)
    (text-color yellow)
    (printf "World")
    (move-cursor-to 10 25)
    (text-color green)
    (printf "From Racket")
    (restore-text-color)
    (restore-cursor)

The above should work on Linux and OSX terminals, but not in the Windows cmd prompt (it works just fine on Windows in the bash prompt from git).

Best Regards,
Alex.

Evžen Wybitul

unread,
Jun 19, 2018, 7:21:34 AM6/19/18
to Racket Users

Pretty cool, didn't know about this! So let's say I now can display a "@" char with whatever color wherever I want. Then there is only a last thing I need to know how to do before I can ditsch the libraries —

1. How do I open a new, clear terminal window from within racket? Kind of like what happens when I use big-bang (a black rectangle in a window opens), but with terminal instead. You know, I don't want to be drawing into the terminal the user inputs his racket main.rkt.

2. Given this terminal window, 

let's say I somehow get to print this into the terminal:

########
#....@.#
#......#
########

Well and now I want 

########
#......#
#....@.#
########

Can I make it so that only the two changing tiles get redrawn, not the entire scene? Normally, the entire rectangle would be drawn "under" the old one; I haven't yet looked into the ANSI wikipage, so maybe there's an answer there — if so, I'm sorry.

Dne úterý 19. června 2018 12:58:15 UTC+2 Alex Harsanyi napsal(a):

Jay McCarthy

unread,
Jun 19, 2018, 7:39:10 AM6/19/18
to Evžen Wybitul, Racket Users
Here is a little example of raart and lux working together to make a
simple "roguelike"

https://github.com/jeapostrophe/raart/blob/master/t/hack.rkt
> --
> You received this message because you are subscribed to the Google Groups
> "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to racket-users...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.



--
-=[ Jay McCarthy http://jeapostrophe.github.io ]=-
-=[ Associate Professor PLT @ CS @ UMass Lowell ]=-
-=[ Moses 1:33: And worlds without number have I created; ]=-

Bert De Ketelaere

unread,
Jun 19, 2018, 7:41:12 AM6/19/18
to Evžen Wybitul, Racket Users
Hello,

maybe this can be of interest to you:
specifically his ascii-canvas

Groeten,
Bert

From: racket...@googlegroups.com <racket...@googlegroups.com> on behalf of Evžen Wybitul <wybitu...@gmail.com>
Sent: Tuesday, June 19, 2018 13:21
To: Racket Users
Subject: [racket-users] Re: How to use Raart to draw in terminal?
 
--

Alex Harsanyi

unread,
Jun 19, 2018, 8:48:39 AM6/19/18
to Racket Users

For point 1, most terminal programs (like vim or less) switch to an alternate buffer in the same terminal and restore the previous contents when they are done.  Like so:



    (define (clear-display) (printf "\e[2J"))
    (define (move-cursor-to x y) (printf "\e[~a;~aH" x y))
    (define (save-cursor) (printf "\e[s"))
    (define (restore-cursor) (printf "\e[u"))
    (define (text-color c) (printf "\e[~am" c))
    (define (restore-text-color) (printf "\e[39m"))
    (define (enable-alt-buffer) (printf "\e[?47h"))
    (define (disable-alt-buffer) (printf "\e[?47l"))


    (define red 31)
    (define green 32)
    (define yellow 33)
    (define blue 34)

    (save-cursor)
    (enable-alt-buffer)

    (clear-display)
    (move-cursor-to 10 10)
    (text-color red)
    (printf "Hello")
    (move-cursor-to 15 15)
    (text-color yellow)
    (printf "World")
    (move-cursor-to 10 25)
    (text-color green)
    (printf "From Racket")
    (restore-text-color)
    (move-cursor-to 20 0)
    (printf "Press any key to exit")
    (flush-output)
    (read-char)
    (clear-display)
    (disable-alt-buffer)
    (restore-cursor)

For point 2, you can already do that by moving the cursor to the desired position and writing an "." or an "@" as needed.

Alex.

Martin DeMello

unread,
Jun 19, 2018, 2:22:04 PM6/19/18
to Evžen Wybitul, Racket Users
Note that opening a new terminal window is something the enclosing environment (e.g. the X server) does, not something the terminal does. A terminal-based app could be running in an environment where the terminal is all there is (e.g. a linux virtual terminal).

martin

--
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscribe@googlegroups.com.

Jon Kleiser

unread,
Jun 19, 2018, 3:39:04 PM6/19/18
to Racket Users
Hi Alex,

These ANSI escape codes works fine in my Mac Terminal. Just a comment re. the final "Press any key to exit": It seems to me it should have been "Press Return to exit". ;-)

/Jon

Neil Van Dyke

unread,
Jun 20, 2018, 3:03:14 AM6/20/18
to Evžen Wybitul, Racket Users
Evžen Wybitul wrote on 06/19/2018 04:16 AM:
> 2) CharTerm — doesn't support colors and unicode, AFAIK.

Correct.  Sorry.  I don't know if/when those features will be added.

The reason for a few glaring feature omissions, while some other things
appear over-engineered,[1] is that I got interrupted with other demands
on my time.

I might get back to the `charterm` package someday, but that might be a
year or more away. (Especially since there's a hobby project I really
want to do, involving a terminal interface on a special kind of homebrew
terminal hardware.  But, for non-hobby reasons, I just finished some
terminal work in Rust, and I'm learning Go at the moment, while
currently in a long-distance relationship with Racket.)

If you want to get fancy, at this time, you might have the best luck
doing the terminal control yourself, atop Racket I/O ports, or doing FFI
to one of the modern Unicode-supporting "curses" libraries, like "ncursesw".

Note about FFI: originally, I made `charterm` avoid FFI entirely
(perhaps partly because around that time I improved the stability and
performance of a large Racket system by removing all the application C
code from the process), but today I might do a little bit of FFI in
`charterm`, to get rid of the "stty" dependency.

[1]
http://www.neilvandyke.org/racket/charterm/#%28part._.Terminal_.Diversity%29

Neil Van Dyke

unread,
Jun 20, 2018, 4:04:32 AM6/20/18
to Evžen Wybitul, Racket Users
Evžen Wybitul wrote on 06/19/2018 07:21 AM:
>
> Pretty cool, didn't know about this! So let's say I now can display a
> "@" char with whatever color wherever I want. Then there is only a
> last thing I need to know how to do before I can ditsch the libraries —
>
> 1. How do I open a new, clear terminal window from within racket? Kind
> of like what happens when I use big-bang (a black rectangle in a
> window opens), but with terminal instead. You know, I don't want to be
> drawing into the terminal the user inputs his /racket main.rkt/.

Like Alex and Martin said, when your Racket program is running in a
terminal, you probably want to use that terminal (saving and restoring
the page, ideally, which most terminals nowadays support).

However, there are a couple ordinary scenarios in which a terminal might
not be available:
* Running a terminal program from a GUI desktop interface that doesn't
create the terminal window automatically.
* Running a terminal program under development from DrRacket, and there
is no terminal window for it to use.

What I'd suggest doing (coincidentally, this was on my Rust TODO list),
is make a package/procedure that, if no terminal is available, it
attempts to create the terminal, in a platform-specific way.  It could
look through a list of possible commands for one that is available
("xdg-terminal", "x-terminal-emulator", "urxvt", "rxvt", "xterm",
"konsole", "gnome-terminal", etc.).  It might be easiest to do portably
across Un*xen by not getting into subtleties of TTYs, and instead using
the command line for that terminal emulator program to invoke another
instance of your Racket program.

> Can I make it so that only the two changing tiles get redrawn, not the
> entire scene? Normally, the entire rectangle would be drawn "under"
> the old one; I haven't yet looked into the ANSI wikipage, so maybe
> there's an answer there — if so, I'm sorry.

Like Alex said, you can do the drawing incrementally from your
application code (the terminal itself has at least one persistent screen
buffer, and this is the interface that ANSI X3.64 provides), and you
don't normally need to refresh/redraw it from application code, like
many GUI drawing interfaces would have you do.

However, two reasons why you might want to use/make a library that *can*
do a complete or partial redraw of the terminal screen, even though the
terminal remembers what's on the screen:
* You can have display corruption, such as due to a window resize, or
other sources of text going to the screen (from, e.g., error messages,
systems notices, or "line noise").
* You might want to implement GUI elements that temporarily obscure part
of the display (e.g., inventory menu in a roguelike), and the redraw them.

You might prefer to use an interface like "curses" that can handle these
redisplays, or implement a library like that yourself.  And it can also
let you make your changes to a display state in the program, and the
library figures out an efficient set of terminal control codes to update
the terminal's display from what the library thinks it currently is. 
(The efficiency of updates usually isn't important anymore, since you
can usually redraw an entire display in an instant over a slow SSH link
from halfway around the world.  But most old hardware terminals on which
you might want to proudly display your roguelike, in your living room,
still max out at around 1000 to 2000 bytes per second when wired direct
to an RS232 port, and a single attribute change takes a few/several
bytes in ANSI, so it could take you a few seconds to redraw a pretty
ordinary display even on a direct wired line.)

BTW, if you're looking for an old terminal for your roguelike, a DEC
VT102 is mostly ANSI, and has a couple important features that a VT100
doesn't, while still being very retro.  A VT220 or VT320 is also a good
choice, more features, not as retro.  There are a ton of other old
terminals, and some with some archaic but neat properties, but, once you
start needing ANSI, they usually look more modern and boring.  (BTW, you
might be able to fit a Raspberry Pi and peripherals inside a VT102
chassis, without interference or overheating, which would be a fun
living room computer appliance for a roguelike, sadistic guest `elinks`
Web browser, Kodi controller, etc.)

Evžen Wybitul

unread,
Jun 21, 2018, 5:13:42 AM6/21/18
to Racket Users
Thank you all for your helpful responses. 

@bedeke I've seen that series, but in the end he himself mentions that the library he built is too slow.

@Alex As my objective now is to make a roguelike in Racket, I think I won't venture into making my own ANSI-code library (although it sounds interesting). I'm glad I learned something new, though; I actually didn't understand how those things (terminal libraries etc.) worked, until now. Thanks!

@Jay I'll use lux for the foreseeable future, with tiles, and add Raart support later. Thank you very much for the provided code examples!

@Neil No problem, we all have our main and not-so-main projects. The one terminal-in-my-livingroom (I guess that's what it is) project of yours sounds fun! I'll think about replicating soem of the behavior you mentioned in Racket, once I finish my current roguelike project. I'm not really into old hardware (I'm not even 20, you know), but you managed to spark a light of interest. I'll look into that sometime.

Once more, thank you all for you help. Have a nice summer!
Reply all
Reply to author
Forward
0 new messages