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
- The first time it is opened, the desktop loads the module and calls its entry once.
- Each time it is opened, the desktop calls
open(oropen_withfor a file). Return a new window, or raise the one you have. - The module stays loaded when its windows close. Threads and state survive, so the next
openfinds everything where it was. - When the
.APPfile changes (an update) and the app has no windows, the next open callsquit, 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.
| Field | Since | What it is for |
|---|---|---|
name | 1 | The app's name (window titles are set by window_new) |
open | 1 | Open a window (or raise the existing one) and return it |
menu | 1 | A menu item of one of its windows was chosen: (window, id) |
closing | 1 | One of its windows is closing: let go of what belongs to it |
open_with | 2 | Open a given file, or a command line after the app's name |
relabel | 3 | The language changed: rebuild labels taken from say() |
quit | 4 | About 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:
| Field | Meaning |
|---|---|
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_ms | A timer: called every repaint_ms, even in the background, then repainted. |
menus, menu_count | A menu strip under the title; choices go to MIZU_APP.menu. |
data | Your pointer, for apps with several windows. |
minimum_width, minimum_height, fixed | Limits 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;
}
- Lists, drop-downs and tabs take items with
control_add(control, text, icon); the chosen one iscontrol->value. Icons come frommizu->icon("FOLDER16.BMP"). - A tree grows with
control_tree_add(tree, parent, text, icon, can_open). An item that can open but has no children yet sendsCONTROL_EXPANDINGwhen it is opened: add them then, so a whole disk need not be read ahead. - Controls on tab pages:
control_page(control, tabs, page)shows them only while that page is chosen. - A dialog of your own: build a window with
fixed = 1, thenmodal(window)runs it until a button callsmodal_end(window, result).
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
notify(title, text, icon, target)(API 22): a balloon over the tray, then a line in the notification list.targetis what a click opens: a file, a folder, or an app such as"PLAYER.APP". Mizugram uses it for new messages.clip_set/clip_getcarry files;clip_image_set/clip_image_get(22) carry a picture,0xRRGGBBpixels, as Paint and screenshots do.
Files and folders
known_folder(which, out, size)(API 24) gives the user's folders: 1 Desktop, 2 Documents, 3 Downloads, 4 Music, 5 Pictures, 6 Videos, and 7 AppData for your own files (caches, settings). Save downloads into Downloads, not beside the app.folder_label(path)gives the name to show for one of them, in the user's language.drag_files(paths, count, icon)(21): call it fromdragwhile the button is held, and the files ride under the pointer to whatever takes them: a Files window, a folder, the desktop, the Recycle Bin, or a window of yours withdrop.open_with_app(file, argument)(25): open another of the desktop's apps with something to open, as Files does with("PLAYER.APP", folder).
Long work
background(work, done, data)(API 17):work(data)runs on a background thread and must not touch windows; thendone(data)runs on the desktop's thread, where it may. Reading a big file, decoding an image.- Your own threads (
koi_thread_start) for work that lives as long as the app: a network connection, audio. Guard shared state with aKOI_LOCK, callmizu->repaint()to show results, and stop them inquit. yield()gives the desktop a turn from inside a long loop, as a last resort.
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.