Lem Advent Calendar 2023 - Frontend/Backend間のInterfaceと各Frontendについて
これは Lem Advent Calendar の記事です。
概要
一日目で紹介したように、LemはBackendとFrontendから構成されています。
Backendがエディタの機能を担い、Frontendはキーボードやマウス入力、画面表示などを担います。
インターフェースはlem-interfaceパッケージ内でgeneric functionとして定義されています。
Frontendではこれらのインターフェースに対応するメソッドを定義していくことになります。
インターフェース詳細
view
windowに対応するviewというオブジェクトが存在します。
viewはbackend/frontend間でwindow情報をやりとりするために使われ、定義は各フロントエンド毎に行われます。
viewはx, y, width, heightなどの値を保持したクラスや構造体となることを想定しています。
メソッド一覧
以下は代表的なメソッドの一覧です。
メソッド名 | 説明 |
---|---|
make-view | 新しいwindowが作られたときに呼び出されます |
delete-view | windowが削除されたときに対応するviewが引数になり呼び出されます |
set-view-pos | windowの位置が変更されるときに呼び出され、引数はview, x, yになります |
set-view-size | windowの大きさが変更されるときに呼び出され、引数はview, width, heightになります |
render-line | 指定のviewのx, yにテキストを描画します |
clear | 指定のviewをクリアします |
clear-to-end-of-window | 指定のviewのy行目以降をクリアします |
get-background-color | フロントエンドの背景色を取得します |
get-foreground-color | フロントエンドの前景色を取得します |
update-foreground | フロントエンドの前景色を変更します |
update-background | フロントエンドの背景色を変更します |
update-cursor-shape | カーソルの形を変更します |
display-width | ウィンドウの幅を取得します |
display-height | ウィンドウの高さを取得します |
display-title | ウィンドウタイトルを取得します |
set-display-title | ウィンドウタイトルを変更します |
display-fullscreen-p | フルスクリーンであるかを取得します |
set-display-fullscreen-p | フルスクリーンの設定をします |
各フロントエンド
ncurses
Terminalでの入出力を行うライブラリとしてncursesがあります。
Common Lispではcl-charmsというライブラリがあり、low-level apiとしてncursesのffiが用意されており、lemではこれを使っています。
最初期から存在し、最も長く保守され使われ続けている実装です。
Electron frontend
かつて、Lemにはelectron frontendが存在しました。
これを実現するためには、Common Lispの外部のプロセスと通信をする必要があります。
そのためフロントエンドの実装の一つにJSON-RPCを用意しました。
JSON-PRCによってelectronのプロセスと通信を行うことでlemにelectronを使うことができるようになりました。
electronを使うことにより、webviewでhtmlを表示することが出来たり、色々と可能性はありましたが、今は保守されておらず動きません。
SDL2
2023年5月のVer 2.0 リリース時に追加した実装です。
このリリースによってlemの可能性は大きく高まりました。
これまでいくつかのフロントエンド(xcb, opengl, capi, electron, etc...)が実験的に作られてきましたが、実際に使えるところまで作り込み、保守されつづけてきた実装はncursesだけでした。
SDL2によってようやくGUI版のLemをリリースできました。
SDL2 frontendには以下の特徴があります。
Windowsサポート
今までLemをwindows上で動かすことは面倒で動作も不完全でした。
2.0から複数のプラットフォームをサポートしているSDL2 frontendを実装したことにより、この問題は解決し、windows用のリリースを実現できました。
マウスのサポート
これまで実用できるfrontendはncursesのみでしたが、ncursesでマウスを使うには制限があります。
例えばncursesでクリックを入力として受け取れますが、ドラッグは入力として受け取れません。
このような理由で正式にマウスのサポートはしていませんでした。
SDL2を使うことにより、この問題は解消され、マウスによる操作が可能になりました。
(当たり前にある機能ですが、全部時前でやろうとすると色々と面倒なのです)
グラフィックス機能
特定のバッファやウィンドウにSDL2の機能を使って図形や画像を表示できます。
以下のgifはその機能を使った例です。
上記のgifで使ったソースコードを以下に貼ります。
(defpackage :graphics
(:use :cl
:lem
:lem-sdl2/graphics))
(in-package :graphics)
(defvar *font*
(sdl2-ttf:open-font
(asdf:system-relative-pathname "lem-sdl2" "resources/fonts/NotoSansMono-Regular.ttf")
100))
(defvar *image*
(load-image
(asdf:system-relative-pathname "lem-sdl2" "resources/icon.png")))
(defvar *buffer* (make-buffer "*example*"))
(clear-drawables *buffer*)
(draw-string *buffer* "Hello World" 10 10 :font *font* :color (make-color 255 255 0))
(draw-image *buffer* *image* :x 200 :y 50)
(loop :for n :from 10 :to 255 :by 20
:do (draw-rectangle *buffer* n n 300 300 :color (make-color 0 0 0))
(draw-rectangle *buffer* n n 300 300 :filled t :color (make-color n n n)))
open-fontやload-imageで事前にリソースを初期化しておき、draw-stringやdraw-image関数で描画できます。
この機能についての詳細は後日まとめます。
おわりに
二日目はこれで終わりです。
書いていると気付くのですが、設計が未熟な点やコードの汚なさが見つかり、改善点が出てきて良いですね。
Discussion