GitHub - skinnyjames/hokusai-pocket: portable ruby guis

4 min read Original article ↗

A project for making portable apps and games using hokusai

Work in progress, expect changes. ideas and contributions are welcome.

installation

Hokusai pocket uses barista for bootstrapping itself.

Note: It is not recommended to build this project with mrbgems.

  • git clone https://github.com/skinnyjames/hokusai-pocket.git && cd hokusai-pocket
  • barista cli
    • this command will compile mruby/tree-sitter/raylib and produce a hokusai-pocket binary in bin/

usage

To run apps using the binary

  • hokusai-pocket run:target=<somefile.rb>
    • where <somefile.rb> is a hokusai app

To package your app as a binary for the host system from this repo

  • hokusai-pocket build:target=<somefile.rb>
    • where <somefile.rb> is a hokusai app

To cross-compile your app for different platforms (wip, requires docker)

  • hokusai-pocket publish:target=<somefile.rb>
    • optional arguments include
      • assets_path=[assets folder]
      • platforms=osx (defaults to osx,linux,windows)
      • extras=[folders accessible to the build] (useful for including custom gems)
      • gem_config=[file declaring conf.gems] (useful for adding gems)

This will create a platforms/[platform]/[target]/[targetfile] for each included platform

basic example

An example app that can be run with hokusai-pocket run:target=counter.rb

# counter.rb
class Counter < Hokusai::Block
  style <<~EOF
  [style]
  additionStyles {
    background: rgb(214, 49, 24);
    cursor: "pointer";
  }

  additionLabel {
    size: 40;
    color: rgb(255,255,255);
  }

  subtractStyles {
    background: rgb(0, 85, 170);
    cursor: "pointer";
  }

  subtractLabel {
    size: 40;
    color: rgb(255, 255, 255);
  }
  EOF

  template <<-EOF
  [template]
    hblock { background="255,255,255" }
      label#count {
        :content="count.to_s"
        size="190" 
        :color="count_color"
      }
    hblock
      vblock#add { ...additionStyles @click="increment"}
        label { 
          content="Add"
          ...additionLabel 
        }
      vblock#subtract { ...subtractStyles @click="decrement" }
        label { 
          content="Subtract"
          ...subtractLabel 
        }
  EOF

  uses(
    vblock: Hokusai::Blocks::Vblock,
    hblock: Hokusai::Blocks::Hblock,
    label: Hokusai::Blocks::Text,
  )

  attr_accessor :count, :keys, :modal_open

  def count_positive
    count > 0
  end

  def increment(event)
    self.count += 1
  end

  def decrement(event)
    self.count -= 1
  end

  def count_color
    count.negative? ? [244, 0, 0] : [0, 0, 244]
  end

  def initialize(**args)
    @count = 0

    super
  end
end

Hokusai::Backend.run(Counter) do |config|
  config.title = "Counter"     # title
  config.fps = 60              # set frames per second
  config.width = 550
  config.height = 500

  # turn on hot reloading with entrypoint file
  # if you wish
  config.hot_reload = "counter.rb"

  config.after_load do         # register fonts
    Hokusai.fonts.register "default", Hokusai::Backend::Font.default
    Hokusai.fonts.activate "default"
  end
end

recipes

  • hokusai-pocket @desktop build a desktop cli with raylib and mruby
  • hokusai-pocket @mobile builds a cli using SDL + Raylib for ARM64 architecture using OpenGL ES2

customizing the build

When building hokusai-pocket, every MRuby gem is embedded into the final binary..

New gems can be mixed in / compiled into the binary by rebuilding hokusai-pocket.

hokusai-pocket cli,hokusai:remote=true mruby:gem_config=./gems tells us a couple things

  • pull hokusai-pocket code from github
  • build mruby with a gem snippet located in the file gems

The gems file is interpolated into the mruby config and looks like this:

conf.gem mgem: "mruby-zlib" 

Since typing this out is tedious, a barista recipe encapsulates this

hokusai-pocket @rebuild

will rebuild the hokusai-pocket binary with new gems

development

First, build or obtain a barista binary

barista cli will initially build the following archives in vendor in addition to the hokusai-pocket binary

* libtree-sitter.a [task: treesitter]
* libraylib.a      [task: raylib]
* libmruby.a       [task: mruby]
* libnfd.a/nfd.lib [task: nfd]
* libuv.a          [task: libuv]
* libhokusai.a     [task: hokusai]

When updating any hokusai code, just run barista hokusai to update libhokusai.a.

To modify the build process for mruby, raylib, or tree-sitter, adjust the Brewfile and rebuild that task.

barista clean will remove the vendor library.

testing

More soon.

dev tour

The project structure is

/bin
  hokusai-pocket              (the binary)
/grammar                      (the tree sitter grammar for the templates)
/ruby                         (the hokusai ruby project)
/mrblib                       (the hokusai ruby project as a single file)
/src                          (supporting c files)
Brewfile                      (the build file for the project)

Notes

hokusai pocket supports a basic implementation of require_relative in the ruby code. It will perform basic subsitution and generate one large ruby file to be compiled using mrbc

Related projects

  • Taylor - A simple game engine built using raylib and mruby
  • Ruby 2D - Make cross-platform 2D applications in Ruby
  • DragonRuby - DragonRuby is a Multilevel Cross-platform Runtime. The “multiple levels” within the runtime allows us to target platforms no other Ruby can target: PC, Mac, Linux, Raspberry Pi, WASM, iOS, Android, Nintendo Switch, PS4, Xbox, and Stadia.