Files
nmemmertandClaude Sonnet 4.6 a9efede702 Phase 4-6: Quill editor, Lens viewer, Rigel context menu, CI, website
Phase 4 — native apps:
- quill/: text editor (monospace, double-buffered, find bar, undo, open/save)
- lens/: image viewer using imlib2 (zoom, pan, rotate, directory nav)
- wm/src/menu.c: launch quill instead of mousepad
- iso/docker/entrypoint.sh: add imlib2 package; install quill + lens binaries
- skykit/template/: developer app template demonstrating the SkyKit API

Phase 5 — file system and search:
- rigel/rigel.c: right-click context menu (open, open in quill, copy path,
  rename, trash, properties); Ctrl+F search/filter bar with name highlighting;
  F2 rename-in-place overlay; Delete key moves to trash

Phase 6 — distribution:
- .github/workflows/build-iso.yml: GitHub Actions CI that compiles all
  components and builds a bootable ISO on every tag push; creates a GitHub
  release with sha256 checksums

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-24 10:07:45 -04:00

147 lines
5.1 KiB
C

/* hello.c — minimal SkyKit application template
*
* Build:
* cd skykit && make (builds libskykit.a)
* cd template
* gcc -O2 hello.c -I.. -L.. -lskykit \
* $(pkg-config --libs --cflags x11 xft fontconfig) -o hello
*
* This template shows the minimum boilerplate for a native OrionOS app:
* - sk_app_init() opens the display and loads fonts
* - Create a window and a Pixmap back-buffer
* - Wrap them in an SKPainter
* - Draw to the Pixmap, then XCopyArea to the window
* - Respond to Expose, KeyPress, ButtonPress, ClientMessage
*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <string.h>
#include "skykit.h"
#define WIN_W 640
#define WIN_H 400
int main(void) {
SKApp app;
sk_app_init(&app);
/* ── Window ──────────────────────────────────────────── */
XSetWindowAttributes swa = {
.background_pixel = sk_color(&app, SK_BG),
.event_mask = ExposureMask | KeyPressMask
| ButtonPressMask | StructureNotifyMask,
};
Window win = XCreateWindow(
app.dpy, app.root,
200, 100, WIN_W, WIN_H, 0,
app.depth, InputOutput, app.vis,
CWBackPixel | CWEventMask, &swa);
/* Back-buffer for flicker-free rendering */
GC gc = XCreateGC(app.dpy, win, 0, NULL);
Pixmap buf = XCreatePixmap(app.dpy, win, WIN_W, WIN_H, app.depth);
/* SKPainter wraps the back-buffer */
SKPainter p = { .app = &app, .drw = buf, .gc = gc };
/* WM_DELETE_WINDOW protocol */
Atom wm_del = XInternAtom(app.dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(app.dpy, win, &wm_del, 1);
XStoreName(app.dpy, win, "Hello OrionOS");
XClassHint *ch = XAllocClassHint();
if (ch) { ch->res_name = (char*)"hello"; ch->res_class = (char*)"Hello"; XSetClassHint(app.dpy, win, ch); XFree(ch); }
XMapWindow(app.dpy, win);
XFlush(app.dpy);
/* Click counter for demo */
int clicks = 0;
/* ── Draw function ───────────────────────────────────── */
/* We define a lambda-style local redraw — in a real app this would be
* a proper function taking your app struct. */
#define REDRAW() do { \
sk_fill(&p, SK_BG, 0, 0, WIN_W, WIN_H); \
\
/* Accent bar at top */ \
sk_fill(&p, SK_ACCENT, 0, 0, WIN_W, 3); \
\
/* Centred heading */ \
const char *title = "Hello, OrionOS"; \
int tw = sk_textw(&app, app.font_hdr, title); \
sk_text(&p, app.font_hdr, SK_TEXT_HDR, \
(WIN_W - tw) / 2, sk_baseline(app.font_hdr, 60, 32), title); \
\
/* Subtitle */ \
const char *sub = "A SkyKit native application"; \
int sw = sk_textw(&app, app.font_ui, sub); \
sk_text(&p, app.font_ui, SK_TEXT_DIM, \
(WIN_W - sw) / 2, sk_baseline(app.font_ui, 100, 24), sub); \
\
/* Click counter button */ \
SKBtn btn = { (WIN_W - 160) / 2, 160, 160, 36, "Click me", 1 }; \
sk_draw_btn(&p, &btn, 0); \
\
/* Counter label */ \
char cnt[64]; snprintf(cnt, sizeof(cnt), "Clicked %d time%s", clicks, clicks==1?"":"s"); \
int cw = sk_textw(&app, app.font_ui, cnt); \
sk_text(&p, app.font_ui, SK_TEXT, (WIN_W - cw) / 2, \
sk_baseline(app.font_ui, 215, 24), cnt); \
\
/* Hint */ \
const char *hint = "Press Q or Esc to quit"; \
int hw = sk_textw(&app, app.font_sm, hint); \
sk_text(&p, app.font_sm, SK_TEXT_DIM, (WIN_W - hw) / 2, \
sk_baseline(app.font_sm, WIN_H - 36, 24), hint); \
\
/* Blit to window */ \
XCopyArea(app.dpy, buf, win, gc, 0, 0, WIN_W, WIN_H, 0, 0); \
XFlush(app.dpy); \
} while (0)
REDRAW();
/* ── Event loop ──────────────────────────────────────── */
int running = 1;
while (running) {
XEvent ev;
XNextEvent(app.dpy, &ev);
switch (ev.type) {
case Expose:
if (ev.xexpose.count == 0) REDRAW();
break;
case KeyPress: {
KeySym ks = XLookupKeysym(&ev.xkey, 0);
if (ks == XK_q || ks == XK_Q || ks == XK_Escape) running = 0;
break;
}
case ButtonPress:
if (ev.xbutton.button == Button1) {
/* check if inside the button */
int bx = (WIN_W - 160) / 2, by = 160;
if (sk_in_rect(ev.xbutton.x, ev.xbutton.y, bx, by, 160, 36)) {
clicks++;
REDRAW();
}
}
break;
case ClientMessage:
if ((Atom)ev.xclient.data.l[0] == wm_del) running = 0;
break;
}
}
#undef REDRAW
sk_app_free(&app);
XFreePixmap(app.dpy, buf);
XFreeGC(app.dpy, gc);
XDestroyWindow(app.dpy, win);
XCloseDisplay(app.dpy);
return 0;
}