# \[ANN\] \[WIP\] TextUserInterfaces.jl

**URL:** <https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405>\
**Category:** Community\
**Created:** [February 3, 2019, 6:07pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405 "2019-02-03T18:07:49Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 3, 2019, 6:07pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/1 "2019-02-03T18:07:49Z")

</div>

Hi guys!

As promised, I am publishing the very first version of the [TextUserInterfaces.jl](https://github.com/ronisbr/TextUserInterfaces.jl).

This is a wrapper for Ncurses with a Julia-like API to build TUIs.

I think we have potential here. With this small code:

```julia
using TextUserInterfaces

# Initialize the Text User Interface.
tui = init_tui()

# Do not echo the typed characters and do no show the cursor.
noecho()
curs_set(0)

# Create the side window that will contain the menu.
win_menu = create_window(LINES()-4, 20, 0, 0; border = true, title = " Menu ")

# Create the bottom window with the instructions.
win_inst = create_window(4, COLS(), LINES()-4, 0; border = false)

window_println(win_inst, """
Press ENTER to move the panel to top.
Press X to hide/show the panel.
Press F1 to exit.
""")

# Create the windows and panels.
win1 = create_window(10, 40, 0, 22; border = true, title = " Panel 1 ")
win2 = create_window(10, 40, 3, 32; border = true, title = " Panel 2 ")
win3 = create_window(10, 40, 6, 42; border = true, title = " Panel 3 ")
win4 = create_window(10, 40, 9, 52; border = true, title = " Panel 4 ")

panels = [create_panel(win1),
          create_panel(win2),
          create_panel(win3),
          create_panel(win4)]

# Add text to the windows.
window_println(win1, 0, "Text with multiple lines\nwith center alignment";
               alignment = :c, pad = 0)

window_println(win2, 0, "Text with multiple lines\nwith left alignment";
               alignment = :l, pad = 1)

window_println(win3, 0, "Text with multiple lines\nwith right alignment";
               alignment = :r, pad = 1)

window_println(win4, 0, "Line with right alignment"; alignment = :r, pad = 1)
window_println(win4, "Line with center alignment"; alignment = :c, pad = 0)
window_println(win4, "Line with left alignment"; alignment = :l, pad = 1)

# Create the menu.
menu = create_menu(["Panel 1", "Panel 2", "Panel 3", "Panel 4"])
set_menu_win(menu,win_menu)
post_menu(menu)

# Refresh all the windows.
refresh()
refresh_all_windows()
update_panels()

# Store which panels are hidden.
hidden_panels = [false; false; false; false]

# Wait for a key and process.
ch,k = jlgetch()

while k.ktype != :F1
    global ch,k

    if !menu_driver(menu, k)
        if ch == 10 || k.value == "x"
            item_name = current_item_name(menu)

            idx = 0

            if item_name == "Panel 1"
                idx = 1
            elseif item_name == "Panel 2"
                idx = 2
            elseif item_name == "Panel 3"
                idx = 3
            elseif item_name == "Panel 4"
                idx = 4
            end

            idx == 0 && continue

            if ch == 10
                !hidden_panels[idx] && move_panel_to_top(panels[idx])
            else
                if hidden_panels[idx]
                    show_panel(panels[idx])
                    hidden_panels[idx] = false
                else
                    hide_panel(panels[idx])
                    hidden_panels[idx] = true
                end
            end
        end
    else
        refresh_window(win_menu)
    end

    update_panels()
    doupdate()

    ch,k = jlgetch()
end

destroy_menu(menu)
destroy_tui()

```

it was possible to construct this:

 ![TextUserInterfaces](https://global.discourse-cdn.com/julialang/original/3X/0/7/0741ec320c4fcd723102a1476875f2ffef987972.gif)

As I mentioned before, I really need help to make this an complete package! There are a lot of things to do, the most important is to make possible to test the package in Travis (even a very simple test of loading `libncurses` is failing on Linux, but not on macOS).

Thus, can anyone help me with this endeavor?

CC: @affans, @Jean_Michel

---

<div class="post-metadata">

**Author:** ![Jean\_Michel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jean_michel/32/8282_2.png) [@Jean\_Michel](https://discourse.julialang.org/u/Jean_Michel)\
**Post date:** [February 3, 2019, 9:12pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/2 "2019-02-03T21:12:09Z")

</div>

Thank you very much for your work. Here are some initial questions:

Here is the list of Ncurses functions I have used in my last Ncurses project (it was in Ruby)

```julia
addch addchstr addstr attron attrset beep begx begy bkgdset box cbreak
clear close close_screen clrtobot clrtocol clrtoeol color_pair cols
curs_set curx cury def_prog_mode def_shell_mode deleteln echo endwin getch
getmouse hline inch inchnstr initscr init_pair insertln keyname keypad
lines maxx maxy mousemask nl nocbreak noecho noraw noutrefresh overwrite
raw refresh reset_prog_mode scrollok setpos start_color stdscr subwin
ungetch vline

```

There are quite a few of them which seem to be missing from your implementation. Is it because you did  
not get around to it, or do you have a reason to omit some?

Another question. I asked for a minimal example. For example, the following Ruby program  
just waits for a mouse click, displays the mouse state and coordinates, and after another character is given quits.

```julia
require "ncurses"
include Ncurses
initscr()
mousemask(BUTTON1_PRESSED|BUTTON1_RELEASED|BUTTON1_CLICKED)
getch()
mv(4,19)
addstr(getmouse().inspect)
refresh()
getch()
endwin()

```

How would this be translated in your framework?  
I ask these questions because I hope that you will let experienced Curses programmers use directly your framework as an implementation of Curses, so you should give as part of your package a ‘raw’ interface (but not too raw; I certain agree that interaction with the ncurses library should be through an object `TUI_WINDOW`).

So can you explain your design?

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 3, 2019, 9:40pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/3 "2019-02-03T21:40:27Z")

</div>

Hi @Jean_Michel,

Thanks!

> [@Jean\_Michel](#):
>
> There are quite a few of them which seem to be missing from your implementation. Is it because you did  
> not get around to it, or do you have a reason to omit some?

Just because I did not add them. I am using the superb meta-programming capabilities of Julia, and wrapping new functions is very easy. Thanks for that list, I will add all of them.

My goal is to provide all the low-level access to Ncurses and also a Julia-like API to make things easier.

> [@Jean\_Michel](#):
>
> Another question. I asked for a minimal example. For example, the following Ruby program  
> just waits for a mouse click, displays the mouse state and coordinates, and after another character is given quits.

I have not implemented yet the mouse interface. My planned scheduled is:

1. Make tests work so that we can register the package;
2. Have a very basic but functional API with windows, menus, panels, and forms.
3. Add color support, possibly a translator between Crayons.jl and Ncurses.
4. Think about how to make input handling (including mouse) easier to the user.

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 4, 2019, 1:06am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/4 "2019-02-04T01:06:39Z")

</div>

Hi guys!

First of all, sorry for the number of posts, I am very excited for the things we can do here 😊

As a real proof of concept of what we can do using Ncurses, I could create a text satellite simulator with asynchronous commands to the interface. It was very nice to see this done using a very small code. The satellite simulation is handle by [SatelliteToolbox.jl](https://github.com/JuliaSpace/SatelliteToolbox.jl). Hence, I can now create a monitor for the simulations that are normally run on workstations in which I have only ssh access.

 ![Satellite_Simulator_small](https://global.discourse-cdn.com/julialang/original/3X/5/9/591957c830ec3cf09b8a31231798462d48f0c875.gif)

The source-code can be seen here: [https://github.com/ronisbr/TextUserInterfaces.jl/blob/master/examples/satellite\_simulator.jl](https://github.com/ronisbr/TextUserInterfaces.jl/blob/master/examples/satellite_simulator.jl)

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [February 4, 2019, 3:12am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/5 "2019-02-04T03:12:54Z")

</div>

> [@Ronis\_BR](#):
>
> - Add color support, possibly a translator between Crayons.jl and Ncurses.

could you support this package for colors?

> **[GitHub - JuliaGraphics/ColorSchemes.jl: colorschemes, colormaps, gradients,...](https://github.com/JuliaGraphics/ColorSchemes.jl)**
>
> colorschemes, colormaps, gradients, and palettes. Contribute to JuliaGraphics/ColorSchemes.jl development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![affans](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/affans/32/11911_2.png) [@affans](https://discourse.julialang.org/u/affans)\
**Post date:** [February 4, 2019, 5:24am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/6 "2019-02-04T05:24:55Z")

</div>

This is great! I would love to help out. My use case is very similar… running large scale simulations over a cluster, and a terminal dashboard would be great to monitor the simulations.

What do you think is a good place for me to start? How does one go about wrapping a C library?

---

<div class="post-metadata">

**Author:** ![Tero\_Frondelius](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tero_frondelius/32/7629_2.png) [@Tero\_Frondelius](https://discourse.julialang.org/u/Tero_Frondelius)\
**Post date:** [February 4, 2019, 8:05am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/7 "2019-02-04T08:05:14Z")

</div>

> [@Ronis\_BR](#):
>
> Make tests work so that we can register the package

Maybe [VisualRegressionTests](https://github.com/JuliaPlots/VisualRegressionTests.jl) package could help you testing your package. I have seen this used to test [GraphPlot](https://github.com/JuliaGraphs/GraphPlot.jl/blob/master/test/runtests.jl) package, but I haven’t used it myself.

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 4, 2019, 10:52am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/8 "2019-02-04T10:52:31Z")

</div>

> [@affans](#):
>
> This is great! I would love to help out. My use case is very similar… running large scale simulations over a cluster, and a terminal dashboard would be great to monitor the simulations.

Good! 🙂

> [@affans](#):
>
> What do you think is a good place for me to start? How does one go about wrapping a C library?

Actually, I do not have a good plan 😊 There is a code using macros and `@eval` to wrap the Ncurses functions for the libraries `libncurses`, `libmenus`, `libforms`, and `libpanels`. If you like, then you can add more functions like the ones asked by @Jean_Michel. Another important thing (that I was not capable of doing yet) is to make the very simple test of loading the libraries work on Travis. I have no idea why it is falling… It works on macOS.

By the way, let me know you Github username so that I can grant access to the repository.

> [@chakravala](#):
>
> could you support this package for colors?

Maybe, I have no idea how the color support will be implemented. Julia uses ANSI escape sequences and Ncurses does not. We have to convert between them (which seems already done in VT100.jl package). However, AFAIK, Ncurses colors are limited to only 8.

> [@Tero\_Frondelius](#):
>
> Maybe [VisualRegressionTests](https://github.com/JuliaPlots/VisualRegressionTests.jl) package could help you testing your package. I have seen this used to test [GraphPlot](https://github.com/JuliaGraphs/GraphPlot.jl/blob/master/test/runtests.jl) package, but I haven’t used it myself.

Thanks! I will take a look at this package.

---

<div class="post-metadata">

**Author:** ![affans](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/affans/32/11911_2.png) [@affans](https://discourse.julialang.org/u/affans)\
**Post date:** [February 4, 2019, 3:57pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/9 "2019-02-04T15:57:24Z")

</div>

```julia
function __init__ ()

    try

        ncurses.libncurses = Libdl.dlopen("libncursesw")

    catch

        ncurses.libncurses = Libdl.dlopen("libncurses")

    end

    try

        ncurses.libform = Libdl.dlopen("libformw")

    catch

        ncurses.libform = Libdl.dlopen("libform")

    end

    try

        ncurses.libmenu = Libdl.dlopen("libmenuw")

    catch

        ncurses.libmenu = Libdl.dlopen("libmenu")

    end

    try

        ncurses.libpanel = Libdl.dlopen("libpanelw")

    catch

        ncurses.libpanel = Libdl.dlopen("libpanel")

    end

end

```

Ahh I see, so here you are opening up the shared ncurses libraries. You don’t have to give me access to the repository. I will submit PRs that you can approve 🙂

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 5, 2019, 5:09pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/10 "2019-02-05T17:09:23Z")

</div>

Good news, after the help of @jpsamaroo I was able to make the simple tests (library load) pass in Linux and macOS. Now, we should just need to find out a good and simple way to actually test the package. I am just afraid that maybe the tests must be platform dependent.

---

<div class="post-metadata">

**Author:** ![jpsamaroo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jpsamaroo/32/46804_2.png) [@jpsamaroo](https://discourse.julialang.org/u/jpsamaroo)\
**Post date:** [February 5, 2019, 5:41pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/11 "2019-02-05T17:41:18Z")

</div>

IMO the best way to handle testing this package is to dump the desired terminal output to a file, saving that as a “good test”, and then do the same thing during tests and compare the two. It’s crude, but should work.

One issue with the above would be if ncurses is unhappy that it’s no longer running under an interactive TTY (totally possible), in which case we’d have to get fancy and use a PTY to capture the output. Not impossible, but definitely somewhat annoying.

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [February 6, 2019, 1:48am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/12 "2019-02-06T01:48:04Z")

</div>

And now we have initial color support!

 ![16](https://global.discourse-cdn.com/julialang/original/3X/3/6/3611ba5ff823b89b47bd2756066623a2b4dc3da9.jpeg)

 ![34](https://global.discourse-cdn.com/julialang/original/3X/0/f/0f283c6819b02a3b76dabf2ea8d27c7925f4b77d.jpeg)

P.S.: Please, don’t judge my complete lack of design sense… 😄

---

<div class="post-metadata">

**Author:** ![Diego\_Javier\_Zea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/diego_javier_zea/32/1858_2.png) [@Diego\_Javier\_Zea](https://discourse.julialang.org/u/Diego_Javier_Zea)\
**Post date:** [February 6, 2019, 10:02am UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/13 "2019-02-06T10:02:06Z")

</div>

[GitHub - JuliaTesting/ReferenceTests.jl: Utility package for comparing data against reference files](https://github.com/Evizero/ReferenceTests.jl) can help, it is used by UnicodePlots.

---

<div class="post-metadata">

**Author:** ![Akooij](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/akooij/32/4952_2.png) [@Akooij](https://discourse.julialang.org/u/Akooij)\
**Post date:** [April 3, 2019, 5:42pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/14 "2019-04-03T17:42:09Z")

</div>

Just wanted to say i like this library, and to keep up the good work! I might see if i can use it for a roguelike game.

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [April 3, 2019, 5:48pm UTC](https://discourse.julialang.org/t/ann-wip-textuserinterfaces-jl/20405/15 "2019-04-03T17:48:18Z")

</div>

Thanks! The development is a little slow, but I will continue to work on it until we get a good version to be released 🙂
