How to create a GUI app using libui-ng on Windows?

Hi

I heard that Windows support has been improved in Crystal 1.11.

Can someone tell me how to create a GUI app using libui-ng and libui.cr?
Are static libraries sufficient instead of shared libraries on Windows?

https://nightly.link/libui-ng/libui-ng/workflows/build/master

(nightly.link keeps GitHub Actions artifact links up to date, a Crystal service created by oprypin)

I started up my Windows PC and tried it out a bit, but I couldn’t figure out how to do it with Windows. I thought it would be faster to ask the question on the forum, listen to what people have to say, and do what they say.

I’m interested in this because I’m writing a libui bindings for Ruby.

1 Like

I’ve used libui (not its fork libui-ng, but API is roughly similar) with macOS environment. I didn’t use the libraries you mentioned, it looks like they only offer low-level API, instead I used iu, a wrapper for the libui library API, which is nice to get started with, even though it’s probably no longer maintained. Basically you just need to layout the required components and register the callbacks.

A GUI application usually can’t run in a single thread, but AFAIK Crystal’s multithreading support on Windows isn’t ready yet, so you’ll need to create your own UI threads and worker threads, and take care that interface operations are performed in the UI threads.

I think so but not tested(on Windows), just make sure the linker can find the static library, then add the -static flag. there should be no need for header files, as these libraries map the API using the C binding.

Other thoughts: libui and libui-ng are great libraries, they have the advantage of being lightweight, but they are also missing many important features. If memory serves, when using libui a few years ago, it even lacked events in response to double clicks on table rows. Now I prefer to develop with imgui, also has pretty great Crystal bindings(thanks @oprypin) and can develop native GUI programs with complex features.

1 Like

Hi @existXFx

I would like to use ui too. But I think libui.cr is running on the backend of iu. So I wanted to run libui.cr first.

libui has been forked into libui-ng and some functions have been added, such as uiTableOnRowDoubleClicked.
ImGui is complicated and doesn’t look very native, which is not really my taste.
But hey, I have to try it at least once.

A GUI application usually can’t run in a single thread, but AFAIK Crystal’s multithreading support on Windows isn’t ready yet, so you’ll need to create your own UI threads and worker threads, and take care that interface operations are performed in the UI threads.

Hmm, this seems quite difficult.

Actually, you don’t need another thread - you can just call Fiber.yield in an idle event or a main loop (not sure how to do it in case of libui-ng). Then other fibers will execute when gui thread gives them time for it.

1 Like

To be honest, I am not sure about something much more basic.

First, download the file from the link.
https://nightly.link/libui-ng/libui-ng/workflows/build/master/Mingw-x64-static-release.zip

Then you will find the file builddir/meson-out/libui.a

How to use libui.a on Windows?

libui.cr is written like this ↓

  1. Do I need to modify @[Link("ui")] ?
  2. How do I specify build options such as --link-flags ?
  3. Is libui.a enough? Do we need additional files such as libui.lib?

*.a is for Linux, *.lib is for Windows. You can use link-flags, but it’ll either be -L/directory/path/of/a (Linux) or /LIBPATH:C:\directory\path\to\lib (Windows).

1 Like

You should have these files somewhere in CRYSTAL_LIBRARY_PATH:

  • libui-static.lib: Static library (built with MSVC)
  • libui-dynamic.lib: DLL import library
  • <name of the DLL>.dll: the actual dynamic library

then put @[Link("ui", dll: "<name of the DLL>.dll")].

1 Like

After much trial and error, I finally succeeded in opening a basic window, but I am not sure what worked and what did not.

require "../src/libui/libui.cr"

o = UI::InitOptions.new
err = UI.init pointerof(o)
if !ui_nil?(err)
  exit 1
end

mainwin = (UI.new_window "Writing Windows apps in Crystal? Don't be silly!", 450, 20, 1).not_nil!

# on_closing = ->(w : UI::Window*, data : Void*) {
#  UI.control_destroy ui_control(mainwin)
#  UI.quit
#  0
# }

UI.window_set_margined mainwin, 1
# UI.window_on_closing mainwin, on_closing, nil

UI.control_show ui_control(mainwin)

UI.main
UI.uninit

image

Besides, I get the following error.

Unhandled exception: passing a closure to C is not allowed (Exception)

So I had to comment out the callback.
This is probably for the reason that @existXFx pointed out.

For a callback, you can check Callbacks - Crystal

as a simple fix - replace mainwin to MAINWIN (so a callback will access a constant, not local variable).