AnotherOS

Documentation

The AO toolkit

The native application framework — an AppKit-shaped C API, callable from C, C++ and Go.

AO_ is the native application framework for AnotherOS. It is one GUI API modelled on macOS's AppKit, callable from C, C++ and Go, drawing through the display protocol and the Kvarcas 2D renderer. It ships as libao.so.1.

The rule that shapes it: no stubs

If it is not documented, the symbol is not in the library. Nothing in AO_ is a stub that returns success and does nothing, and the declared-versus-defined count is checked mechanically on every build.

This is worth stating because it changes how you read the rest of this page. A missing feature that reads as a present one is worse than an absent feature: it fails at the moment it matters and reports success everywhere else. If a call is here, it does the thing.

The stack

your application
      AO_            widgets, layout, responder chain, run loop
    Kvarcas          2D rendering — paths, text, images
   libfasadas        the display protocol transport
     fasadasd        the compositor / window server

Core concepts

Concept Behaviour
Coordinates y-down from the top-left of your content.
Layout Springs and struts — autoresizingMask. A resize lays out; it does not scale.
Events A responder chain, AppKit-modelled.
Target/action One signature, deliberately.
Windows Closing a window is a request. Teardown happens once the dispatch depth returns to zero, so you may close a window from inside one of its own handlers.
Run loop Yours. Timers, file descriptors and background work hang off it; AO_ never takes the main loop away from you.
Preferences AO_UserDefaults. A suite is a .conf file; reads are cached, writes are immediate.
Modifier keys AO_CurrentModifierFlags(), and a click carries its own modifiers — which is what lets a window that is not key, and the desktop and dock which never are, see a Control-click.
Drawing by hand A custom view with a drawRect and a mouse handler is the sanctioned escape hatch, not a private toolkit.

Presented frames

AO_WindowDelegate.did_present(win, time_ms, user) fires once per frame the compositor has actually put on the scanout, and AO_WindowPresentedFrameCount(win) is its pull-based twin.

This is the only frame signal that reflects what reached the panel. A benchmark or an animation that paces on its own draw loop is counting something faster than the screen, and will report numbers the user cannot see.

Borderless windows, moving and resizing

AO_WindowCreateBorderless() is the equivalent of NSWindowStyleMaskBorderless: the compositor draws no chrome and your application draws its own.

Such a window then moves with AO_WindowPerformDrag(win) and resizes from a grow box with AO_WindowPerformResize(win, AO_WINDOW_EDGE_*), both called from a primary-button mouse_down. Both hand the press to the compositor's own gesture rather than faking a move from your motion events — motion events jitter by a frame, and a window dragged from them visibly lags the pointer. Both return 0 against a compositor that does not advertise interactive gestures.

AO_WindowFrameOrigin() and AO_WindowSetFrameOrigin() read and request the content origin, and AO_WindowDelegate.did_move reports every change. Together they are what an application needs to reopen a window where the user left it. The compositor still places new windows; restoring one is a different act.

Fonts and the system typeface

Ask for a role, never a size or a family.

Role px For
AO_FONT_MENU 14 menu bar and menu items
AO_FONT_REGULAR 13 controls, labels, list rows, body — the default
AO_FONT_SMALL 11 help text, sidebar headings, status bars, icon labels
AO_FONT_MINI 9 annotations and badges — rarely the right answer
AO_FONT_TITLE 13 window titles: body size, heavier weight
AO_FONT_HEADING 17 section headings inside a window body
AO_FONT_DISPLAY 26 About-box product names

Sizes are device pixels, one number one pixel, matching how macOS specifies its system font at 72 dpi.

A bare 11.0 in your source records how big something looked on the day it was typed, and the next reader cannot tell whether it may be changed. A role can.

AO_FONT_FAMILY is a sentinel, not a family name. A system typeface ships as two optical cuts — SF Pro Text and Display, Inter and Inter Display — and the size chooses between them. AO_FontCreate() picks the cut from the size you pass. Naming a face by hand gets you cramped large text or loose small text, which is the most visible way to render either family wrongly.

The user picks the typeface in System Preferences → Appearance → System font (SF Pro, Inter or DejaVu Sans). It is read once per process, so a running application keeps the face it started with.

Widgets

Class Notes
AO_Control The base: target/action, enable and validate.
AO_Button One class, seven types.
AO_TextField Caret, selection, real clipboard.
AO_Slider, AO_ProgressIndicator
AO_Box A group with a real interior.
AO_Image, AO_ImageView
AO_ScrollView, AO_ClipView, AO_Scroller Scroll wheel supported.
AO_TextView Many lines, a real undo stack.
AO_Menu, AO_MenuItem Global menu bar, key equivalents, validation.
AO_PopUpButton, fly-outs, context menus Each panel is a real popup surface: it leaves the window and flips at the work-area edges, with keyboard and type-select.
AO_Alert
AO_OpenPanel, AO_SavePanel Real sheets. They answer Shift-Cmd-period themselves and share one Show Hidden Files setting with the file manager.
AO_SegmentedControl A row of exclusive choices, three tracking modes; a single segment can be disabled.
AO_TableView, AO_TableColumn A data source, multiple selection, a double action, an optional header — and it scrolls.
AO_Pasteboard Typed, system-wide Cut/Copy/Paste: files, text, images, lazy promises.
AO_UserNotification, AO_UserNotificationCenter Desktop notifications.

Context menus

Right-click, Control-click, or a trackpad's two-finger click.

A view either has a static menu (AO_ViewSetMenu) or builds one per press (AO_ViewHandlers.menu_for_event, the equivalent of menuForEvent:). right_mouse_down runs first, so your selection can follow the click before the menu is built from it.

Show Hidden Files

Shift-Cmd-period is a system gesture, not a checkbox on your window. On macOS it reveals dotfiles in the Finder and in every open and save panel at once, so the state is not in the file manager and not in a save panel — it is in the toolkit, and every file list reads the same one.

int  AO_ShowsHiddenFiles(void);            /* 0 by default: hiding            */
void AO_SetShowsHiddenFiles(int shows);    /* sets, persists, tells observers */
int  AO_ShowsHiddenFilesRefresh(void);     /* has another process changed it? */
int  AO_FileNameIsHidden(const char *dir, const char *name);
int  AO_RootNameIsVisible(const char *name);
void AO_ShowsHiddenFilesAddObserver(AO_HiddenFilesChangedFn fn, void *user);
void AO_ShowsHiddenFilesRemoveObserver(AO_HiddenFilesChangedFn fn, void *user);

Two hiding rules, both stated once in AO_FileNameIsHidden():

  1. a leading dot hides, by convention;
  2. at the root of the system disk, only Applications, Users and System are shown.

Rule 2 hides; it does not skip. An entry the user is not being shown is still an entry, which is what lets the hotkey be a redraw rather than a re-read of the disk — and what makes revealing it possible at all.

If you show a file list of your own: read AO_ShowsHiddenFiles() rather than testing for a leading dot; register an observer so your list follows a toggle made elsewhere in your process; and call AO_ShowsHiddenFilesRefresh() once per run-loop turn so it follows one made in another process. The refresh throttles itself, so calling it every turn costs nothing.

The pasteboard

Modelled on NSPasteboard. AO_PasteboardGeneral() is a singleton handle; nothing is released. A board holds one item with several typed representations, and the compositor keeps the bytes — so a copy survives the process that made it.

AO_Pasteboard *pb = AO_PasteboardGeneral();
AO_PasteboardClearContents(pb);                          /* take ownership */
AO_PasteboardSetData(pb, AO_PASTEBOARD_TYPE_PNG, png, n);
AO_PasteboardSetData(pb, AO_PASTEBOARD_TYPE_STRING, "a picture", 9);

size_t len;
char *bytes = AO_PasteboardDataForType(pb, AO_PASTEBOARD_TYPE_PNG, &len, 500);
AO_Free(bytes);
  • Promises. AO_PasteboardDeclareTypes() names types without producing them. Your provide callback runs when somebody pastes one, and for every outstanding type when your process exits normally.
  • Ownership. AO_PasteboardSetData returns −1 once somebody else has copied. It never writes into their copy.
  • Text. AO_PasteboardSetString and AO_PasteboardString are the same clipboard that GTK, Qt, Firefox and Win32 programs use.
  • Files. AO_PasteboardSetFiles() and AO_PasteboardSetFilesOp() write three representations: an RFC 2483 text/uri-list of percent-encoded file:/// URLs, the paths as plain text one per line so that pasting into an editor gives you paths, and the copy-or-move intent.

From a menu validator, call AO_PasteboardHasTypeCached() and never AO_PasteboardHasType(). The plain form asks the compositor and blocks for the answer, so a validator that calls it costs one synchronous round trip per run-loop turn — dozens a second on an idle desktop. The cached form re-asks only when the change count has moved, and answers "unknown, stay enabled" against a compositor too old to push notifications. After writing the board from your own process, call AO_PasteboardInvalidateTypeCache(): your own write is not in the compositor's count until its notification comes back.

Do not build your own cache keyed on "the change count is not zero". A count of zero means both an old compositor and a healthy board nobody has copied to yet, so such a cache never hits from boot until the user's first Copy.

HiDPI

A window has a backing scale factor. Ask for it rather than assuming 1, draw at the backing size, and let the compositor present it. Sizes in the font table above are device pixels at scale 1.

What is deliberately absent

There is no private widget toolkit hiding behind AO_, no retained scene graph, and no shader language in the drawing path. When a widget does not exist, the answer is a custom view with a drawRect, which is a supported and ordinary thing to write.

Bindings

The C API is the API. C++ and Go bindings call the same libao.so.1 and add no behaviour of their own.