やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

RubyでGUIが触れるglimmer-dsl-libuiの紹介と内部実装について

先日、SubscribeしているRuby Weeklyからメールが届きました。
何やらRubyでGUIが触れる「glimmer-dsl-libui」たる、とても面白そうなgemが紹介されていました。
このgemは福岡Ruby2022でスペシャルアワードを受賞しており、とても注目されているgemのようです。

github.com

GUIを触ろうと思うと、低レイヤーが扱える言語が対象に上がるイメージがありますが、何とRubyでGUIが作れてしまう...というのが驚きです。 どんなgemなのか気になったので少し触ってみました。

gemのインストールのみでGUIが作れる

何よりも驚いたのがglimmer-dsl-libuiのみをインストールすればRubyでGUIを触ることが可能という点です。
だいたい何かしらの低レイヤー言語で実装された依存ライブラリを事前にインストール・アップデートをしないと、こういったライブラリは使えない認識ですが、事前準備は特に不要とのこと...。

githubのREADME.mdを参考に簡単なサンプルを動かしてみます。
Gemfileを更新してbundle installを行っただけの状態ですが、果たして問題なく動くのでしょうか...。

# frozen_string_literal: true

source "https://rubygems.org"

gem 'glimmer-dsl-libui', '~> 0.10.2'
require 'glimmer-dsl-libui'

include Glimmer

window('hello world', 300, 200) {
  button('Button') {
    on_clicked do
      puts 'Button Clicked'
    end
  }
}.show

おぉ、何か出ましたよ。ウィンドウ(300x200)にButtonと表示されたボタンが描画されています。
先のサンプルコードの通り、GUIが構成されています。 コードを見る感じ、ボタンのクリック時にハンドラーが仕込まれているようなので、ボタンをクリックしてみます。
おそらく、コンソール上に「Button Clicked」と表示されるはずです。

$ bundle exec ruby main.rb 
# ボタンを押下後...
Button Clicked

思った通りです。各要素にハンドラーを仕込んでRubyの処理を実行させることも可能でした。
もう少し複雑なサンプルも記載されていたので、そちらも動作させてみます。個人情報を登録・検索可能な簡易なアプリケーションのようです。 コード量が多いため、コード転載は割愛します。

おぉ、先ほどのサンプルと比べるとかなりリッチなGUIが描画されました。
軽く触ってみたところ、データの追加・検索も問題なく動いていました。これは凄いですね。他にもテトリスやスネークゲームといったサンプルまで紹介されており、このgemの可能性を感じます。

https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-snake.gif

どうやって作っているのか

RubyでどのようにGUIが触れるように実現しているのでしょうか。
ピュアなRubyだけでは実装が難しいように思えます。gemの依存関係を調べていくと「libui」というC言語で実装されたGUIライブラリに辿り着きました。

github.com

このライブラリ自体がさまざまな言語で使用可能なように、各言語用のライブラリを提供していました。
Ruby用のライブラリも存在しておりglimmer-dsl-libuiは、Ruby用のライブラリをgemとしてインストールして内部で使用しているようです。
となると、どこかでC言語で実装されているlibuiをRuby側で読み込んでいる箇所があるはずです。
先ほどのライブラリのソースコードを追ってみると、該当のコードを発見することができました。

lib/libui/ffi.rb

begin
  dlload LibUI.ffi_lib
rescue LoadError
  raise LoadError, 'Could not find libui shared library'
end

LibUI/lib/libui/ffi.rb at main · kojix2/LibUI · GitHub

あまり、見慣れない構文ですがdlloadというメソッドを使うことで、C言語のライブラリをインポートすることができるんですね。今まで全く使う機会がなかったです...。

require 'fiddle/import'

module Sample
  extend Fiddle::Importer
  dlload "/usr/lib/libc.dylib"
  extern "int printf(char*, ...)", :printf, :varargs
end

Sample.printf("Hello world! from Ruby!\n")
# Hello world! from Ruby!

Fiddle::Importer#dlload (Ruby 3.2 リファレンスマニュアル)

最後に

Ruby Weeklyにて紹介されていたglimmer-dsl-libuiを試してみました。
まずRubyでGUIが触れるというのに驚かされました。デスクトップアプリを作るならElectron一択かな...と思っていたのですが、Rubyでも作れる可能性があると考えるだけでワクワクします。
文法もSwiftUIのようで、非常に可読性が良いと感じました。

実装の中身についても非常に面白かったです。
C言語でGUIの基本的な処理をライブラリ化して、それらをRubyから使用すれば、確かにOSのグラフィックAPIを使用することが可能です。 ただ直接、Rubyから呼び出すのは難しいという壁を感じつつ、Ruby向けにライブラリ化してくれている方には頭が上がりません...。

少しでも「ええな〜」と思ったらはてなスター・はてなブックマーク・シェアを頂けると励みになります。