Writing an app

An app is a module the desktop loads and calls. It gets a table of functions, MIZU_API, and gives back a description of itself, MIZU_APP. Its windows are drawn by the desktop's frames, in its theme and language.

The smallest one

#include "mizu.h"

static const MIZU_API* mizu;

static void paint(WINDOW* window, int x, int y, int width, int height) {
    mizu->label(x + 10, y + 10, "Hello from Mizu!", mizu->color(MIZU_COLOR_TEXT));
}

static WINDOW* open(void) {
    WINDOW* window = mizu->window_new("Hello", 40, 40, 300, 200);
    if (window) window->paint = paint;
    return window;
}

static MIZU_APP me = { "Hello", MIZU_APP_VERSION, open };

MIZU_APPLICATION(start)
static MIZU_APP* start(const MIZU_API* api) { mizu = api; return &me; }
./koicc -m hello.c       # HELLO.APP

Open HELLO.APP from the Run box or Files. mizu.h and window.h are in the SDK.

How an app lives

  1. The first time it is opened, the desktop loads the module and calls its entry once.
  2. Each time it is opened, the desktop calls open (or open_with for a file). Return a new window, or raise the one you have.
  3. The module stays loaded when its windows close. Threads and state survive, so the next open finds everything where it was.
  4. When the .APP file changes (an update) and the app has no windows, the next open calls quit, unloads the old module and loads the new one.

Everything runs on the desktop's thread: while a handler runs, the desktop waits. Return quickly; for long work see below.

MIZU_APP

Fields arrive in versions; set version to MIZU_APP_VERSION and leave what you do not need as null.

FieldSinceWhat it is for
name1The app's name (window titles are set by window_new)
open1Open a window (or raise the existing one) and return it
menu1A menu item of one of its windows was chosen: (window, id)
closing1One of its windows is closing: let go of what belongs to it
open_with2Open a given file, or a command line after the app's name
relabel3The language changed: rebuild labels taken from say()
quit4About to be unloaded: stop your threads, return 1 when they are stopped

Declaring version 4 is a promise: once its windows are closed, nothing but the app's own threads runs its code. In return, updates reach it without a restart.

Windows

window_new returns a WINDOW; fill in what you need:

FieldMeaning
paint(window, x, y, width, height)Draw the contents. The rectangle is the client area, in screen coordinates.
click(window, x, y, clicks)Left button, relative to the client area; clicks is 2 for a double click.
key(window, key)A character, or a KOI_KEY_* above 0xFF.
context(window, x, y)Right button. mizu->context_menu opens a menu there.
drag(window, x, y)The pointer moving with the left button held after a press inside.
release(window, x, y)The left button let go after that press (API 21).
drop(window, x, y, paths)Files dropped on the window, full paths one a line (API 21). Unset: nothing can be dropped there.
wheel(window, notches)Wheel, positive upwards. Unset: sent as arrow keys.
tick(window) and repaint_msA timer: called every repaint_ms, even in the background, then repainted.
menus, menu_countA menu strip under the title; choices go to MIZU_APP.menu.
dataYour pointer, for apps with several windows.
minimum_width, minimum_height, fixedLimits on resizing; fixed makes a dialog: close box only, no resizing.
window->menus[0] = (WINDOW_MENU){ "File", { { "New", 1 }, { 0, 0 }, { "Close", 2 } }, 3 };
window->menu_count = 1;

After changing what a window shows outside its own handlers, call mizu->repaint().

Drawing

Inside paint, use the koi_gfx_* calls from koi.h and the desktop's own pieces: label and label_styled for text in the window font, raised and sunken for 3D edges, scrollbar for a scroll bar that matches, and color(MIZU_COLOR_*) for the theme's face, text, paper and accent colours. Using the theme's colours keeps the app right in every colour scheme.

Controls

Since API 18 the desktop draws and drives the standard controls for you: buttons, labels, text fields, check boxes, radio buttons, lists with icons, drop-down lists, sliders, progress bars, group frames, tabs and (19) trees. Focus, Tab, Enter for the default button and Esc for the cancel one all work without a line from you. What the user did comes to the control's event.

static CONTROL* name;

static void greet(CONTROL* button, int what) {
    mizu->message("Hello", mizu->control_text(name), "OK");
}

static WINDOW* open(void) {
    WINDOW* window = mizu->window_new("Greeter", 80, 80, 320, 140);
    CONTROL* button;

    mizu->control_new(window, CONTROL_LABEL, 10, 12, 80, 20, "Name:");
    name = mizu->control_new(window, CONTROL_FIELD, 90, 10, 200, 24, "Koi");
    button = mizu->control_new(window, CONTROL_BUTTON, 200, 50, 90, 26, "Greet");
    button->event = greet;
    button->is_default = 1;                                  /* Enter presses it */
    button->anchor = CONTROL_ANCHOR_RIGHT | CONTROL_ANCHOR_BOTTOM; /* follows resizing */
    mizu->control_set_tip(button, "Says hello");             /* API 19 */
    return window;
}

Asking things

confirm, message and prompt show the desktop's dialogs; pick_file its file browser. They return when the user answers, and the desktop keeps running underneath. say(SAY_*) gives standard phrases in the machine's language.

The common dialogs

pick_file and (18) save_file show Open and Save: a folder tree with the user's folders and drives on the left, the files on the right. choose_color picks a colour. All three return 1 when accepted.

Telling the user

Files and folders

Long work

static int stop, stopped;

static void network(void* unused) {
    while (!__atomic_load_n(&stop, __ATOMIC_SEQ_CST)) {
        /* ... wait for data with a short timeout ... */
    }
    __atomic_store_n(&stopped, 1, __ATOMIC_SEQ_CST);
}

static int quit(void) {
    __atomic_store_n(&stop, 1, __ATOMIC_SEQ_CST);
    for (int i = 0; i < 100; i++) {
        if (__atomic_load_n(&stopped, __ATOMIC_SEQ_CST)) return 1;
        koi_sleep(20);
    }
    return 0;   /* still busy: stay loaded for now */
}

Versions

mizu->version is the MIZU_API_VERSION of the desktop you are running on. Before using a field newer than 1, check it: an app built today may run on an older desktop. The MIZU_API reference says which version each field arrived in.

The interface is frozen since version 33 (Mizu 1.0): fields are only added at the end, and none moves, changes meaning or goes away, so an app built today keeps running on later desktops. Changing the system's own settings (language, colours, wallpaper, the screen, remote desktop, shutting down) is for the applications of the system: their fields do nothing for any other app, and mizu->system is null for it.

A real example of all of this is Mizugram, a Telegram client: a network thread, quit, a list with a scroll bar, and text input.

Packages →