ExNcursesで遊ぼう。その１

# 1　OverView

さて、Cursesであります。
あ、受け狙いじゃなくて、僕がElixirを理解するために、必要な経験だと思って書いております。

まずは、Cursesから紹介したいと思います。Cursesと呼んでますが、現在主流となっているのはncurses。

ncurses（new curses）は、ターミナルベースのユーザーインターフェースを作成するためのライブラリです。
もともとはSystem V Release 4.0（SVr4）のcursesライブラリのエミュレーションとして開発されましたが、
現在では多くの機能拡張が行われています

端末ベースのアプリ、特にvimやEmacsなどのテキストエディタでお世話になっていると思います。
https://invisible-island.net/ncurses/announce.html

見覚えあるUNIXのアプリケーション、多くないですか？

こいつをElixirから使得る様になるのが、ExNcursesです。

https://hexdocs.pm/ex_ncurses/ExNcurses.html

今回は、どういうものか？を先に書こうと思います。

| 回　| 内容 |
|:------------|--------------------------------------------------------------------------------|
|第一回|Elixirへのex_ncursesへのインストール説明<br>|
|第二回|ElixirのAPIの説明|
しなくてはなりません|
|第三回|Elixirで、スカッシュゲームを作ってみよう|
|第四回|ExNCursesのサンプルコード解説|

# 2. 解説なんぞを。
## 2.1 まずは、ExNCursesの解説から。

最初に、一番ややこしいところを説明しましょう。読み飛ばしてくれて構いません。
ぶっちゃけ、後でわかるｗ

まずは、hexdocの引用から行きましょう。
https://hexdocs.pm/ex_ncurses/ExNcurses.html

```
ExNcurses lets Elixir programs create text-based user interfaces using ncurses.

ExNcursesは、Elixirプログラムがncursesを使用してテキストベースのユーザーインターフェースを作成できるようにします。
```

続いて、
```
Aside from keyboard input, ExNcurses looks almost like a straight translation of the C-based ncurses API. ExNcurses sends key events via messages. 

キーボード入力を除けば、ExNcursesはCベースのncurses APIのほぼ直訳のように見えます。ExNcursesはキーイベントをメッセージとして送信します。
```

「ExNcursesはCベースのncurses APIのほぼ直訳のように見えます」
ここが重要で、非常に簡単に、テキストベースのWindowを出すアプリケーションを構築出来ます。

また、「ExNcursesはキーイベントをメッセージとして送信します」と言う部分についてですが、これまた、簡単です。
画面に対して、キーが押された場合、

ハンドルするコードは、以下の様なものになります。　抜粋すると…
```
 # ターミナルからの入力を待ち受けます。ユーザーがキーを押すのを待ち、その入力を処理します。
 ExNcurses.listen()


 receive do
        # :ex_ncursesのプロセスから、イベントを受け取っている
        {:ex_ncurses, :key, key} ->
          handle_key(state, key)
 end


 # wasdそれぞれのキーをハンドルする、上記のhandle_keyと、関数の引数keyでパターンマッチングしている。
 defp handle_key(state, ?w), do: %{state | direction: :up}
 defp handle_key(state, ?a), do: %{state | direction: :left}
 defp handle_key(state, ?s), do: %{state | direction: :down}
 defp handle_key(state, ?d), do: %{state | direction: :right}

```

…これだけです。うん、これだけなんだ、「あ、Elixirの機能知らんと出来ないな」ってところは。

## 2.2 そしてサンプル…みたら驚くよ。

では、Hello worldから行きましょう。

https://github.com/jfreeze/ex_ncurses/blob/master/examples/emoji.exs

```
Application.ensure_started(:ex_ncurses)

ExNcurses.initscr()

ExNcurses.mvprintw(4, 8, "Hello, 🐄\n")
ExNcurses.refresh()
ExNcurses.getch()
ExNcurses.endwin()
```

一行ずつ解説していきます。
ぶっちゃけ、Copilotに解説させた方が早いってくらいにシンプルです。

1. Application.ensure_started(:ex_ncurses):<br>
   この行は、:ex_ncurses アプリケーションが開始されていることを確認します。ライブラリを使用する前に初期化するために必要なステップです。
1. ExNcurses.initscr(): <br>
   描画用の画面を初期化します。ExNcurses を使用する際に最初に呼び出す関数です。
1. ExNcurses.mvprintw(4, 8, "Hello, 🐄\n"): <br>
   この関数はカーソルを画面上の位置 (4, 8) に移動し、文字列 "Hello, 🐄\n" を表示します。座標 (4, 8) は画面上の行と列の位置を表します。
1. ExNcurses.refresh():<br>
   この関数は画面を更新して、行われた変更を反映させます。新しい内容を表示するためにこの関数を呼び出す必要があります。
1. ExNcurses.getch():<br>
   この関数はユーザーの入力（キー押下）を待ちます。プログラムを一時停止して、ユーザーがキーを押すのを待つ役割を果たします。
1. ExNcurses.endwin(): <br>
    この関数は ncurses セッションを終了し、ターミナルを通常の状態に戻します。ncurses の使用が終わったら最後に呼び出す関数です。


くれぐれも1.のensure_startedを忘れない様にお願いします。
このプロセスが、Elixirのアプリケーションと、端末のキーの入力などを取り持ちます。

さて、ここからは本当に簡単、ExNcurses.initscr()で画面を初期化したら、その初期化した画面に対して、描画(上の例ではExNcurses.mvprintw)、描画を追えたら、ExNcurses.refresh/0で画面をリフレッシュしたら、画面に文字列が表示されます。

この、描画する関数を呼び出す -> 全部呼び出すのが終わったら、リフレッシュを言う流れを覚えてください。難しいのはここだけですね。


## 2.3 ウィンドウについて

Linuxの端末なのに、複数の画面に分かれてるアプリケーション、見たことないですな？
え？見たことない？ではEmacsを…とか言うと怒られるので、サンプルを実行してみましょう。

マルチ画面にする前に、まずは、端末の画面の中に、新しい画面を作成し、そこに対して"Hello, Window!"と描画してみましょう。

```
defmodule NcursesExample do
  def run do
    Application.ensure_started(:ex_ncurses)
    ExNcurses.initscr()

    win = ExNcurses.newwin(10, 20, 5, 5) # 新しいウィンドウの作成
    # winウィンドウ内のカーソル位置を(y, x)に移動します。これにより、次に描画される文字の位置が指定されます。
    ExNcurses.wmove(win, 1, 1)
    ExNcurses.waddstr(win, "Hello, Window!") # ウィンドウに文字列を表示
    ExNcurses.wrefresh(win)              # ウィンドウを更新
    ExNcurses.getch()                    # キー入力を待つ
    ExNcurses.delwin(win)                # ウィンドウを削除

    ExNcurses.endwin()
  end
end

NcursesExample.run()
```

表示する座標が変わってることに気づきでしょうか？

新しい関数を紹介しておきましょう。

```
ExNcurses.newwin(nlines, ncols, begin_y, begin_x)
```
説明: 新しいウィンドウを作成します。

パラメータ:

- nlines: ウィンドウの行数
- ncols: ウィンドウの列数
- begin_y: ウィンドウの開始位置（y座標）
- begin_x: ウィンドウの開始位置（x座標）

戻り値: 作成されたウィンドウのポインタ

```
ExNcurses.wmove(win, y, x)
```
説明: 指定したウィンドウ内でカーソルを新しい位置に移動します。

パラメータ:
- win: ウィンドウのポインタ
- y: 新しい行位置
- x: 新しい列位置

戻り値: 成功した場合は0、失敗した場合はエラーコード

```
ExNcurses.wprintw(win, "Hello, Window!")
```

説明: 指定したウィンドウに文字列を書き込みます。

パラメータ:

- win: ウィンドウのポインタ
- str: 書き込む文字列

戻り値: 成功した場合は0、失敗した場合はエラーコード

これらの関数を使用することで、ex_ncursesライブラリを使ったウィンドウ操作が可能になります。詳細はこちらで確認できます。

では、お待ちかね、Windowを増やしてみましょう。

```
defmodule NcursesExample do
  def run do
    Application.ensure_started(:ex_ncurses)
    ExNcurses.initscr()

    win1 = ExNcurses.newwin(10, 20, 5, 5) # 新しいウィンドウの作成
    win2 = ExNcurses.newwin(10, 20, 5, 26) # 新しいウィンドウの作成
    # winウィンドウ内のカーソル位置を(y, x)に移動します。これにより、次に描画される文字の位置が指定されます。
    ExNcurses.wmove(win1, 1, 1)
    ExNcurses.wmove(win2, 1, 1)

    ExNcurses.waddstr(win1, "Hello, Window1!") # ウィンドウに文字列を表示
    ExNcurses.waddstr(win2, "Hello, Window2!") # ウィンドウに文字列を表示

    ExNcurses.wrefresh(win1)              # ウィンドウを更新
    ExNcurses.wrefresh(win2)              # ウィンドウを更新
    ExNcurses.getch()                    # キー入力を待つ
    ExNcurses.delwin(win1)                # ウィンドウを削除
    ExNcurses.delwin(win2)                # ウィンドウを削除

    ExNcurses.endwin()
  end
end

NcursesExample.run()
```

Windowsが二枚に増えましたね。
Hello, windows1とHello, windows2が別々のWindowsに「それぞれの座標に」描写されている
ことにご注意ください。


# まとめ

さて、今回は端末への描写、Windowの作成をやってみました。
出来るのと便利は違う、と怒られそうなサンプルばかりになりましたね。

では、次回は「Elixirで、スカッシュゲームを作ってみよう」と題して、便利であることを
ごらんに入れたいと思います。

ほら、あれですよ！あれ！

「一週間待ってください、本物のEx_ncursesを御覧に入れますよ」

って山岡士郎がかっこつけるやつです。。。うん、がんばるね。