- Rename all binaries: skywm→orionwm, skydeskbar→oriondeskbar, skylauncher→orionlauncher, skyinstall/skyinstall-gui→orioninstall* - Rename theme directory: themes/SkyOS/ → themes/OrionOS/ - Update all string literals, window titles, menu labels, and comments - Update ISO build: hostname→orionos, user→orion, all paths/labels - Add Plymouth boot splash theme (orionos) with Navy+Blue palette - Update GTK2/GTK3 theme names, wallpaper output name - Update .gitignore, root Makefile, build-iso.sh for new binary names Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1125 lines
41 KiB
C
1125 lines
41 KiB
C
/*
|
||
* OrionOS GUI Installer — Xlib/Xft wizard
|
||
*
|
||
* Steps:
|
||
* 0 Welcome
|
||
* 1 Select disk
|
||
* 2 User & hostname
|
||
* 3 Review
|
||
* 4 Installing (live log + progress bar)
|
||
* 5 Done / reboot
|
||
*/
|
||
|
||
#include <X11/Xlib.h>
|
||
#include <X11/Xutil.h>
|
||
#include <X11/Xatom.h>
|
||
#include <X11/keysym.h>
|
||
#include <X11/Xft/Xft.h>
|
||
#include <dirent.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <poll.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/wait.h>
|
||
#include <unistd.h>
|
||
|
||
/* ── window geometry ──────────────────────────────────────────────── */
|
||
#define WIN_W 660
|
||
#define WIN_H 520
|
||
#define HDR_H 70 /* title header height */
|
||
#define FOOT_H 56 /* button footer height */
|
||
#define BODY_Y HDR_H
|
||
#define BODY_H (WIN_H - HDR_H - FOOT_H)
|
||
|
||
/* ── colors ──────────────────────────────────────────────────────── */
|
||
#define C_BG 0x12161F /* window background */
|
||
#define C_HDR 0x0E1828 /* header panel */
|
||
#define C_HDR_ACCENT 0x1C4A80 /* header accent stripe */
|
||
#define C_BODY 0x161A26 /* body background */
|
||
#define C_FOOT 0x0E1220 /* footer */
|
||
#define C_PANEL 0x1A2030 /* card / panel bg */
|
||
#define C_INPUT_BG 0x0C1018 /* text field bg */
|
||
#define C_INPUT_BD 0x263040 /* text field border */
|
||
#define C_INPUT_ACT 0x2060A0 /* active field border */
|
||
#define C_BTN 0x1C3A60 /* button fill */
|
||
#define C_BTN_HOV 0x2060A0 /* button hover */
|
||
#define C_BTN_PRI 0x2060A0 /* primary button */
|
||
#define C_BTN_PRI_H 0x2878C0 /* primary button hover */
|
||
#define C_BTN_DNG 0x6A1818 /* danger button */
|
||
#define C_BTN_DNG_H 0x9A2020 /* danger hover */
|
||
#define C_RADIO 0x1C3A60 /* radio fill */
|
||
#define C_RADIO_SEL 0x2060A0 /* radio selected */
|
||
#define C_SEP 0x1E2840 /* separator */
|
||
#define C_TXT 0xC8D4E8 /* primary text */
|
||
#define C_TXT_DIM 0x5A7090 /* dim text */
|
||
#define C_TXT_INP 0xD8E4F8 /* input text */
|
||
#define C_TXT_HDR 0xE8F0FF /* header text */
|
||
#define C_TXT_LOGO 0x88AADD /* "OrionOS" accent */
|
||
#define C_WARN 0xFFCC44 /* warning text */
|
||
#define C_ERR 0xFF5555 /* error text */
|
||
#define C_OK 0x44CC77 /* success text */
|
||
#define C_BAR_BG 0x1A2030 /* progress bar bg */
|
||
#define C_BAR_FG 0x2060A0 /* progress bar fill */
|
||
#define C_BAR_SHINE 0x2878C0 /* bar highlight */
|
||
#define C_BORDER 0x1E2840 /* outer border */
|
||
|
||
/* ── fonts ───────────────────────────────────────────────────────── */
|
||
#define F_LOGO "Sans:size=18:weight=bold"
|
||
#define F_HEAD "Sans:size=11:weight=bold"
|
||
#define F_BODY "Sans:size=10"
|
||
#define F_SMALL "Sans:size=9"
|
||
#define F_MONO "DejaVu Sans Mono:size=9"
|
||
#define F_BTN "Sans:size=10:weight=bold"
|
||
|
||
/* ── limits ──────────────────────────────────────────────────────── */
|
||
#define MAX_DISKS 8
|
||
#define MAX_LOG 200
|
||
#define LOG_COLS 80
|
||
#define TF_MAX 128
|
||
|
||
/* ── steps ───────────────────────────────────────────────────────── */
|
||
typedef enum {
|
||
STEP_WELCOME = 0,
|
||
STEP_DISK,
|
||
STEP_USER,
|
||
STEP_REVIEW,
|
||
STEP_INSTALL,
|
||
STEP_DONE,
|
||
} Step;
|
||
|
||
/* ── disk info ───────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
char name[32]; /* sda, nvme0n1, … */
|
||
char path[40]; /* /dev/sda */
|
||
char model[64];
|
||
char size_str[16]; /* "256 GB" */
|
||
long long size_mb;
|
||
} Disk;
|
||
|
||
/* ── text field ──────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
char buf[TF_MAX];
|
||
int len;
|
||
int password; /* show bullets */
|
||
int focused;
|
||
} TF;
|
||
|
||
/* ── log buffer ──────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
char lines[MAX_LOG][LOG_COLS + 1];
|
||
int head; /* next write index (circular) */
|
||
int count; /* total lines written */
|
||
} LogBuf;
|
||
|
||
static void log_push(LogBuf *lb, const char *line) {
|
||
snprintf(lb->lines[lb->head], LOG_COLS, "%s", line);
|
||
lb->head = (lb->head + 1) % MAX_LOG;
|
||
if (lb->count < MAX_LOG) lb->count++;
|
||
}
|
||
|
||
/* ── main state ──────────────────────────────────────────────────── */
|
||
typedef struct {
|
||
Display *dpy;
|
||
int scr;
|
||
Window win;
|
||
GC gc;
|
||
Colormap cmap;
|
||
Visual *vis;
|
||
int depth;
|
||
|
||
XftFont *f_logo, *f_head, *f_body, *f_small, *f_mono, *f_btn;
|
||
|
||
Step step;
|
||
int prev_step;
|
||
|
||
/* disk selection */
|
||
Disk disks[MAX_DISKS];
|
||
int ndisks;
|
||
int sel_disk;
|
||
|
||
/* user fields */
|
||
TF tf_host, tf_user, tf_pass, tf_pass2;
|
||
TF *focused_tf;
|
||
|
||
/* button hover */
|
||
int hov_back, hov_next;
|
||
|
||
/* install progress */
|
||
int progress; /* 0–100 */
|
||
char prog_msg[128];
|
||
LogBuf log;
|
||
int install_fd; /* pipe fd to installer subprocess */
|
||
int install_done;
|
||
char install_err[256];
|
||
|
||
/* error display */
|
||
char err_msg[256];
|
||
} Ins;
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* X helpers */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static unsigned long mkcolor(Ins *s, unsigned long rgb) {
|
||
XColor c;
|
||
c.red = ((rgb >> 16) & 0xFF) * 257;
|
||
c.green = ((rgb >> 8) & 0xFF) * 257;
|
||
c.blue = ((rgb) & 0xFF) * 257;
|
||
c.flags = DoRed | DoGreen | DoBlue;
|
||
XAllocColor(s->dpy, s->cmap, &c);
|
||
return c.pixel;
|
||
}
|
||
|
||
static void xftcol(Ins *s, unsigned long rgb, XftColor *out) {
|
||
XRenderColor rc = {
|
||
((rgb >> 16) & 0xFF) * 257,
|
||
((rgb >> 8) & 0xFF) * 257,
|
||
((rgb) & 0xFF) * 257,
|
||
0xFFFF
|
||
};
|
||
XftColorAllocValue(s->dpy, s->vis, s->cmap, &rc, out);
|
||
}
|
||
|
||
static void fill_rect(Ins *s, int x, int y, int w, int h, unsigned long rgb) {
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, rgb));
|
||
XFillRectangle(s->dpy, s->win, s->gc, x, y, w, h);
|
||
}
|
||
|
||
static void draw_line(Ins *s, int x1, int y1, int x2, int y2, unsigned long rgb) {
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, rgb));
|
||
XDrawLine(s->dpy, s->win, s->gc, x1, y1, x2, y2);
|
||
}
|
||
|
||
/* Draw text and return pixel width */
|
||
static int xft_text(Ins *s, XftDraw *xd, XftFont *f, unsigned long rgb,
|
||
int x, int y, const char *str) {
|
||
if (!str || !str[0]) return 0;
|
||
XftColor col; xftcol(s, rgb, &col);
|
||
XGlyphInfo ext;
|
||
XftTextExtentsUtf8(s->dpy, f, (const FcChar8 *)str, strlen(str), &ext);
|
||
XftDrawStringUtf8(xd, &col, f, x, y + ext.height - ext.y,
|
||
(const FcChar8 *)str, strlen(str));
|
||
XftColorFree(s->dpy, s->vis, s->cmap, &col);
|
||
return ext.width;
|
||
}
|
||
|
||
static int text_w(Ins *s, XftFont *f, const char *str) {
|
||
XGlyphInfo e;
|
||
XftTextExtentsUtf8(s->dpy, f, (const FcChar8 *)str, strlen(str), &e);
|
||
return e.width;
|
||
}
|
||
static int text_h(Ins *s, XftFont *f) {
|
||
XGlyphInfo e;
|
||
XftTextExtentsUtf8(s->dpy, f, (const FcChar8 *)"Ag", 2, &e);
|
||
return e.height;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Widget drawing */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
/* Button: returns 1 if (mx,my) is inside */
|
||
static void draw_button(Ins *s, XftDraw *xd, int x, int y, int w, int h,
|
||
const char *label, unsigned long bg, int hov) {
|
||
unsigned long face = hov ? (bg + 0x101828) : bg;
|
||
/* clamp */
|
||
if ((face & 0xFF) > 0xFF) face = (face & 0xFFFF00) | 0xFF;
|
||
|
||
fill_rect(s, x, y, w, h, face);
|
||
|
||
/* top highlight */
|
||
unsigned long hi = (face < 0xEFEFEF) ? face + 0x101010 : 0xFFFFFF;
|
||
draw_line(s, x, y, x+w-1, y, hi);
|
||
draw_line(s, x, y, x, y+h-1, hi);
|
||
/* bottom shadow */
|
||
draw_line(s, x, y+h-1, x+w-1, y+h-1, C_BORDER);
|
||
draw_line(s, x+w-1, y, x+w-1, y+h-1, C_BORDER);
|
||
|
||
int tw = text_w(s, s->f_btn, label);
|
||
int th = text_h(s, s->f_btn);
|
||
xft_text(s, xd, s->f_btn, C_TXT_HDR,
|
||
x + (w - tw) / 2,
|
||
y + (h - th) / 2,
|
||
label);
|
||
}
|
||
|
||
static void draw_textfield(Ins *s, XftDraw *xd, int x, int y, int w,
|
||
TF *tf, const char *placeholder) {
|
||
int focused = tf->focused;
|
||
unsigned long bord = focused ? C_INPUT_ACT : C_INPUT_BD;
|
||
|
||
fill_rect(s, x, y, w, 32, C_INPUT_BG);
|
||
/* border */
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, bord));
|
||
XDrawRectangle(s->dpy, s->win, s->gc, x, y, w - 1, 31);
|
||
|
||
/* content */
|
||
if (tf->len == 0 && !focused && placeholder) {
|
||
XRectangle clip = { (short)(x+6), (short)(y+2),
|
||
(unsigned short)(w-12), 28 };
|
||
XftDraw *xd2 = xd;
|
||
XftDrawSetClipRectangles(xd2, 0, 0, &clip, 1);
|
||
xft_text(s, xd, s->f_body, C_TXT_DIM, x+6, y+6, placeholder);
|
||
XftDrawSetClip(xd2, None);
|
||
} else {
|
||
/* build display string */
|
||
char display[TF_MAX * 3 + 4];
|
||
if (tf->password) {
|
||
int i; for (i = 0; i < tf->len; i++) {
|
||
/* UTF-8 bullet U+2022 */
|
||
display[i*3+0] = (char)0xE2;
|
||
display[i*3+1] = (char)0x80;
|
||
display[i*3+2] = (char)0xA2;
|
||
}
|
||
display[i*3] = '\0';
|
||
} else {
|
||
memcpy(display, tf->buf, tf->len);
|
||
display[tf->len] = '\0';
|
||
}
|
||
|
||
XRectangle clip = { (short)(x+6), (short)(y+2),
|
||
(unsigned short)(w-12), 28 };
|
||
XftDrawSetClipRectangles(xd, 0, 0, &clip, 1);
|
||
xft_text(s, xd, s->f_body, C_TXT_INP, x+6, y+6, display);
|
||
XftDrawSetClip(xd, None);
|
||
|
||
/* cursor */
|
||
if (focused) {
|
||
int cw = text_w(s, s->f_body, display);
|
||
int cx = x + 6 + cw;
|
||
if (cx > x + w - 8) cx = x + w - 8;
|
||
fill_rect(s, cx, y + 6, 2, 20, C_INPUT_ACT);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Radio button row */
|
||
static void draw_radio(Ins *s, XftDraw *xd, int x, int y, int w,
|
||
const char *label, const char *sub,
|
||
int selected, int hovered) {
|
||
unsigned long bg = selected ? C_RADIO_SEL :
|
||
hovered ? C_RADIO + 0x080808 : C_RADIO;
|
||
fill_rect(s, x, y, w, 44, bg);
|
||
|
||
/* top border */
|
||
draw_line(s, x, y, x+w-1, y,
|
||
selected ? C_BTN_PRI : C_SEP);
|
||
|
||
/* radio circle */
|
||
int cx = x + 20, cy = y + 22;
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, C_SEP));
|
||
XDrawArc(s->dpy, s->win, s->gc, cx-8, cy-8, 16, 16, 0, 360*64);
|
||
if (selected) {
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, C_BTN_PRI));
|
||
XFillArc(s->dpy, s->win, s->gc, cx-5, cy-5, 10, 10, 0, 360*64);
|
||
}
|
||
|
||
xft_text(s, xd, s->f_body, C_TXT, x+38, y+8, label);
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, x+38, y+24, sub);
|
||
}
|
||
|
||
/* Progress bar */
|
||
static void draw_progress(Ins *s, int x, int y, int w, int h, int pct) {
|
||
fill_rect(s, x, y, w, h, C_BAR_BG);
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, C_BORDER));
|
||
XDrawRectangle(s->dpy, s->win, s->gc, x, y, w-1, h-1);
|
||
|
||
if (pct > 0) {
|
||
int fw = (w - 2) * pct / 100;
|
||
if (fw > 0) {
|
||
fill_rect(s, x+1, y+1, fw, h-2, C_BAR_FG);
|
||
/* shine on top half */
|
||
fill_rect(s, x+1, y+1, fw, (h-2)/2, C_BAR_SHINE);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Disk detection */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void detect_disks(Ins *s) {
|
||
s->ndisks = 0;
|
||
DIR *d = opendir("/sys/block");
|
||
if (!d) return;
|
||
struct dirent *de;
|
||
while ((de = readdir(d)) && s->ndisks < MAX_DISKS) {
|
||
const char *n = de->d_name;
|
||
if (n[0] == '.') continue;
|
||
/* skip loop, ram, sr devices */
|
||
if (strncmp(n, "loop", 4) == 0) continue;
|
||
if (strncmp(n, "ram", 3) == 0) continue;
|
||
if (strncmp(n, "sr", 2) == 0) continue;
|
||
if (strncmp(n, "zram", 4) == 0) continue;
|
||
|
||
Disk *dk = &s->disks[s->ndisks];
|
||
snprintf(dk->name, sizeof(dk->name), "%s", n);
|
||
snprintf(dk->path, sizeof(dk->path), "/dev/%s", n);
|
||
|
||
/* size from /sys/block/<name>/size (512-byte sectors) */
|
||
char spath[128];
|
||
snprintf(spath, sizeof(spath), "/sys/block/%s/size", n);
|
||
FILE *f = fopen(spath, "r");
|
||
long long sectors = 0;
|
||
if (f) { fscanf(f, "%lld", §ors); fclose(f); }
|
||
dk->size_mb = sectors / 2048; /* sectors → MB */
|
||
if (dk->size_mb < 4000) continue; /* skip tiny devices <4GB */
|
||
|
||
if (dk->size_mb >= 1024*1024)
|
||
snprintf(dk->size_str, sizeof(dk->size_str),
|
||
"%.1f TB", dk->size_mb / 1024.0 / 1024.0);
|
||
else
|
||
snprintf(dk->size_str, sizeof(dk->size_str),
|
||
"%lld GB", dk->size_mb / 1024);
|
||
|
||
/* model */
|
||
snprintf(spath, sizeof(spath), "/sys/block/%s/device/model", n);
|
||
f = fopen(spath, "r");
|
||
dk->model[0] = '\0';
|
||
if (f) {
|
||
fgets(dk->model, sizeof(dk->model), f);
|
||
fclose(f);
|
||
/* trim trailing newline/spaces */
|
||
int l = strlen(dk->model);
|
||
while (l > 0 && (dk->model[l-1] == '\n' ||
|
||
dk->model[l-1] == ' '))
|
||
dk->model[--l] = '\0';
|
||
}
|
||
if (!dk->model[0]) {
|
||
/* NVMe — try nvme/model */
|
||
snprintf(spath, sizeof(spath), "/sys/block/%s/device/model", n);
|
||
snprintf(dk->model, sizeof(dk->model), "Disk");
|
||
}
|
||
|
||
s->ndisks++;
|
||
}
|
||
closedir(d);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Layout helpers */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void draw_header(Ins *s, XftDraw *xd, const char *title) {
|
||
fill_rect(s, 0, 0, WIN_W, HDR_H, C_HDR);
|
||
fill_rect(s, 0, 0, WIN_W, 3, C_HDR_ACCENT);
|
||
draw_line(s, 0, HDR_H-1, WIN_W, HDR_H-1, C_BORDER);
|
||
|
||
xft_text(s, xd, s->f_logo, C_TXT_LOGO, 20, 12, "OrionOS");
|
||
int lw = text_w(s, s->f_logo, "OrionOS");
|
||
draw_line(s, 20 + lw + 12, HDR_H/2, WIN_W - 20, HDR_H/2, C_SEP);
|
||
xft_text(s, xd, s->f_head, C_TXT_HDR, 20 + lw + 20, 18, title);
|
||
}
|
||
|
||
static void draw_footer(Ins *s, XftDraw *xd,
|
||
const char *back_lbl, const char *next_lbl,
|
||
unsigned long next_col) {
|
||
int fy = WIN_H - FOOT_H;
|
||
fill_rect(s, 0, fy, WIN_W, FOOT_H, C_FOOT);
|
||
draw_line(s, 0, fy, WIN_W, fy, C_BORDER);
|
||
|
||
/* step dots */
|
||
int ndots = 5; /* steps 0-4 shown as dots (5 is done) */
|
||
int dot_step = (int)s->step < ndots ? (int)s->step : ndots-1;
|
||
for (int i = 0; i < ndots; i++) {
|
||
int dx = WIN_W/2 - (ndots*14)/2 + i*14 + 7;
|
||
int dy = fy + FOOT_H/2;
|
||
unsigned long dc = (i == dot_step) ? C_BTN_PRI : C_SEP;
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, dc));
|
||
XFillArc(s->dpy, s->win, s->gc, dx-4, dy-4, 8, 8, 0, 360*64);
|
||
}
|
||
|
||
int bw = 120, bh = 36, by = fy + (FOOT_H - bh) / 2;
|
||
|
||
if (back_lbl)
|
||
draw_button(s, xd, 20, by, bw, bh, back_lbl,
|
||
C_BTN, s->hov_back);
|
||
if (next_lbl)
|
||
draw_button(s, xd, WIN_W - bw - 20, by, bw, bh, next_lbl,
|
||
next_col, s->hov_next);
|
||
}
|
||
|
||
static int in_rect(int mx, int my, int x, int y, int w, int h) {
|
||
return mx>=x && mx<x+w && my>=y && my<y+h;
|
||
}
|
||
|
||
static int back_rect(int *x, int *y, int *w, int *h) {
|
||
*x=20; *y=WIN_H-FOOT_H+(FOOT_H-36)/2; *w=120; *h=36; return 1;
|
||
}
|
||
static int next_rect(int *x, int *y, int *w, int *h) {
|
||
*x=WIN_W-140; *y=WIN_H-FOOT_H+(FOOT_H-36)/2; *w=120; *h=36; return 1;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Validation */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static int validate_step(Ins *s) {
|
||
s->err_msg[0] = '\0';
|
||
switch (s->step) {
|
||
case STEP_DISK:
|
||
if (s->sel_disk < 0) {
|
||
snprintf(s->err_msg, sizeof(s->err_msg), "Please select a disk.");
|
||
return 0;
|
||
}
|
||
break;
|
||
case STEP_USER:
|
||
if (s->tf_host.len < 1) {
|
||
snprintf(s->err_msg, sizeof(s->err_msg), "Hostname cannot be empty.");
|
||
return 0;
|
||
}
|
||
if (s->tf_user.len < 1) {
|
||
snprintf(s->err_msg, sizeof(s->err_msg), "Username cannot be empty.");
|
||
return 0;
|
||
}
|
||
if (s->tf_pass.len < 6) {
|
||
snprintf(s->err_msg, sizeof(s->err_msg),
|
||
"Password must be at least 6 characters.");
|
||
return 0;
|
||
}
|
||
if (strcmp(s->tf_pass.buf, s->tf_pass2.buf) != 0) {
|
||
snprintf(s->err_msg, sizeof(s->err_msg), "Passwords do not match.");
|
||
return 0;
|
||
}
|
||
break;
|
||
default: break;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Step rendering */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void draw_welcome(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, "Welcome");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
int y = BODY_Y + 30;
|
||
xft_text(s, xd, s->f_head, C_TXT_HDR, 40, y,
|
||
"Welcome to the OrionOS Installer");
|
||
y += 36;
|
||
|
||
const char *lines[] = {
|
||
"This wizard will guide you through installing OrionOS",
|
||
"onto your computer.",
|
||
"",
|
||
"What you will need:",
|
||
" • A target disk of at least 16 GB",
|
||
" • About 10–20 minutes",
|
||
"",
|
||
"The target disk will be completely erased.",
|
||
"Make sure you have backed up any important data",
|
||
"before continuing.",
|
||
};
|
||
for (int i = 0; i < (int)(sizeof(lines)/sizeof(lines[0])); i++) {
|
||
unsigned long col = (lines[i][0] == '\0') ? C_TXT :
|
||
(lines[i][0] == ' ') ? C_TXT :
|
||
C_TXT;
|
||
xft_text(s, xd, s->f_body, col, 40, y, lines[i]);
|
||
y += 22;
|
||
}
|
||
|
||
/* EFI/BIOS detection note */
|
||
y += 10;
|
||
int uefi = (access("/sys/firmware/efi", F_OK) == 0);
|
||
char boot_note[80];
|
||
snprintf(boot_note, sizeof(boot_note),
|
||
"Boot mode detected: %s", uefi ? "UEFI" : "Legacy BIOS");
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, 40, y, boot_note);
|
||
|
||
draw_footer(s, xd, NULL, "Next →", C_BTN_PRI);
|
||
}
|
||
|
||
static void draw_disk(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, "Select Installation Disk");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
xft_text(s, xd, s->f_body, C_TXT_DIM, 30, BODY_Y + 16,
|
||
"Choose the disk where OrionOS will be installed. It will be erased.");
|
||
|
||
int y = BODY_Y + 42;
|
||
int dw = WIN_W - 60;
|
||
|
||
if (s->ndisks == 0) {
|
||
xft_text(s, xd, s->f_body, C_ERR, 30, y,
|
||
"No suitable disks found (≥4 GB).");
|
||
} else {
|
||
for (int i = 0; i < s->ndisks; i++) {
|
||
char label[80], sub[80];
|
||
snprintf(label, sizeof(label), "/dev/%s — %s",
|
||
s->disks[i].name, s->disks[i].size_str);
|
||
snprintf(sub, sizeof(sub), "%s", s->disks[i].model[0]
|
||
? s->disks[i].model : "Unknown model");
|
||
draw_radio(s, xd, 30, y, dw, label, sub,
|
||
s->sel_disk == i, 0);
|
||
y += 46;
|
||
}
|
||
}
|
||
|
||
if (s->err_msg[0])
|
||
xft_text(s, xd, s->f_body, C_ERR, 30, WIN_H - FOOT_H - 24,
|
||
s->err_msg);
|
||
|
||
draw_footer(s, xd, "← Back", "Next →", C_BTN_PRI);
|
||
}
|
||
|
||
static void draw_user(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, "Create Your Account");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
int lx = 30, fx = 200, fw = WIN_W - fx - 30;
|
||
int y = BODY_Y + 24;
|
||
int fh = 32, gap = 48;
|
||
|
||
struct { const char *label; TF *tf; const char *ph; } fields[] = {
|
||
{ "Hostname:", &s->tf_host, "orionos" },
|
||
{ "Username:", &s->tf_user, "orion" },
|
||
{ "Password:", &s->tf_pass, "••••••••" },
|
||
{ "Confirm password:", &s->tf_pass2, "••••••••" },
|
||
};
|
||
|
||
for (int i = 0; i < 4; i++) {
|
||
xft_text(s, xd, s->f_body, C_TXT_DIM, lx,
|
||
y + (fh - text_h(s, s->f_body)) / 2, fields[i].label);
|
||
draw_textfield(s, xd, fx, y, fw, fields[i].tf, fields[i].ph);
|
||
y += gap;
|
||
}
|
||
|
||
/* hints */
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y + 8,
|
||
"Password must be at least 6 characters.");
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y + 24,
|
||
"You can change these settings after installation.");
|
||
|
||
if (s->err_msg[0])
|
||
xft_text(s, xd, s->f_body, C_ERR, lx, WIN_H - FOOT_H - 24,
|
||
s->err_msg);
|
||
|
||
draw_footer(s, xd, "← Back", "Next →", C_BTN_PRI);
|
||
}
|
||
|
||
static void draw_review(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, "Ready to Install");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
/* summary card */
|
||
int cx = 30, cy = BODY_Y + 20, cw = WIN_W - 60, ch = 150;
|
||
fill_rect(s, cx, cy, cw, ch, C_PANEL);
|
||
draw_line(s, cx, cy, cx+cw, cy, C_SEP);
|
||
|
||
int lx = cx + 24, rx = cx + 180;
|
||
int y = cy + 18;
|
||
int lh = 26;
|
||
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y, "Disk:");
|
||
{
|
||
char v[80];
|
||
snprintf(v, sizeof(v), "/dev/%s (%s — %s)",
|
||
s->disks[s->sel_disk].name,
|
||
s->disks[s->sel_disk].size_str,
|
||
s->disks[s->sel_disk].model[0]
|
||
? s->disks[s->sel_disk].model : "?");
|
||
xft_text(s, xd, s->f_body, C_TXT, rx, y, v);
|
||
}
|
||
y += lh;
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y, "Hostname:");
|
||
xft_text(s, xd, s->f_body, C_TXT, rx, y, s->tf_host.buf);
|
||
y += lh;
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y, "Username:");
|
||
xft_text(s, xd, s->f_body, C_TXT, rx, y, s->tf_user.buf);
|
||
y += lh;
|
||
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, y, "Boot mode:");
|
||
xft_text(s, xd, s->f_body, C_TXT, rx, y,
|
||
(access("/sys/firmware/efi", F_OK) == 0) ? "UEFI (systemd-boot)"
|
||
: "BIOS (GRUB)");
|
||
|
||
/* warning */
|
||
int wy = cy + ch + 20;
|
||
fill_rect(s, cx, wy, cw, 54, 0x2A1010);
|
||
draw_line(s, cx, wy, cx+cw, wy, 0x6A1818);
|
||
xft_text(s, xd, s->f_head, C_WARN, cx+16, wy+8,
|
||
"⚠ This will erase all data on the selected disk!");
|
||
char wline[128];
|
||
snprintf(wline, sizeof(wline),
|
||
" /dev/%s (%s) will be permanently reformatted.",
|
||
s->disks[s->sel_disk].name,
|
||
s->disks[s->sel_disk].size_str);
|
||
xft_text(s, xd, s->f_body, C_ERR, cx+16, wy+30, wline);
|
||
|
||
draw_footer(s, xd, "← Back", "Install", C_BTN_DNG);
|
||
}
|
||
|
||
/* ── install step ────────────────────────────────────────────────── */
|
||
#define LOG_LINES_VISIBLE 12
|
||
#define LOG_LINE_H 17
|
||
|
||
static void draw_install(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, s->install_done ? "Installation Complete" :
|
||
"Installing OrionOS…");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
int y = BODY_Y + 16;
|
||
|
||
/* progress bar */
|
||
draw_progress(s, 30, y, WIN_W - 60, 26, s->progress);
|
||
y += 30;
|
||
|
||
/* progress percentage + message */
|
||
char pct_str[16];
|
||
snprintf(pct_str, sizeof(pct_str), "%d%%", s->progress);
|
||
xft_text(s, xd, s->f_head, C_TXT, 30, y, pct_str);
|
||
xft_text(s, xd, s->f_body, C_TXT_DIM,
|
||
30 + text_w(s, s->f_head, pct_str) + 12, y, s->prog_msg);
|
||
y += 28;
|
||
|
||
/* log panel */
|
||
fill_rect(s, 30, y, WIN_W - 60,
|
||
LOG_LINES_VISIBLE * LOG_LINE_H + 8, C_PANEL);
|
||
draw_line(s, 30, y, WIN_W-30, y, C_BORDER);
|
||
|
||
int logy = y + 6;
|
||
int start = (s->log.count > LOG_LINES_VISIBLE)
|
||
? (s->log.head - LOG_LINES_VISIBLE + MAX_LOG) % MAX_LOG
|
||
: (s->log.head - s->log.count + MAX_LOG) % MAX_LOG;
|
||
|
||
int show = s->log.count < LOG_LINES_VISIBLE ? s->log.count : LOG_LINES_VISIBLE;
|
||
for (int i = 0; i < show; i++) {
|
||
int idx = (start + i) % MAX_LOG;
|
||
const char *line = s->log.lines[idx];
|
||
unsigned long col = C_TXT_DIM;
|
||
if (strncmp(line, ">>>", 3) == 0) col = C_TXT;
|
||
if (strncmp(line, " ✓", 5) == 0) col = C_OK;
|
||
if (strncmp(line, "ERROR", 5) == 0 ||
|
||
strncmp(line, "error", 5) == 0) col = C_ERR;
|
||
|
||
XRectangle clip = { 32, (short)logy,
|
||
(unsigned short)(WIN_W - 64),
|
||
(unsigned short)LOG_LINE_H };
|
||
XftDrawSetClipRectangles(xd, 0, 0, &clip, 1);
|
||
xft_text(s, xd, s->f_mono, col, 36, logy, line);
|
||
XftDrawSetClip(xd, None);
|
||
logy += LOG_LINE_H;
|
||
}
|
||
|
||
if (s->install_done) {
|
||
if (s->install_err[0]) {
|
||
xft_text(s, xd, s->f_body, C_ERR, 30, WIN_H - FOOT_H - 24,
|
||
s->install_err);
|
||
}
|
||
draw_footer(s, xd, NULL, "Reboot", C_OK);
|
||
}
|
||
/* else: no buttons while installing */
|
||
}
|
||
|
||
static void draw_done(Ins *s, XftDraw *xd) {
|
||
draw_header(s, xd, "Installation Complete");
|
||
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
|
||
|
||
int cy = BODY_Y + BODY_H / 2 - 60;
|
||
xft_text(s, xd, s->f_logo, C_OK,
|
||
WIN_W/2 - text_w(s, s->f_logo, "✓ OrionOS Installed!")/2,
|
||
cy, "✓ OrionOS Installed!");
|
||
cy += 48;
|
||
const char *l1 = "OrionOS has been installed successfully.";
|
||
const char *l2 = "Remove the USB drive and restart your computer.";
|
||
xft_text(s, xd, s->f_body, C_TXT,
|
||
WIN_W/2 - text_w(s, s->f_body, l1)/2, cy, l1);
|
||
cy += 26;
|
||
xft_text(s, xd, s->f_body, C_TXT_DIM,
|
||
WIN_W/2 - text_w(s, s->f_body, l2)/2, cy, l2);
|
||
|
||
draw_footer(s, xd, NULL, "Reboot Now", C_BTN_PRI);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Main draw dispatch */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void redraw(Ins *s) {
|
||
XftDraw *xd = XftDrawCreate(s->dpy, s->win, s->vis, s->cmap);
|
||
fill_rect(s, 0, 0, WIN_W, WIN_H, C_BG);
|
||
|
||
switch (s->step) {
|
||
case STEP_WELCOME: draw_welcome(s, xd); break;
|
||
case STEP_DISK: draw_disk(s, xd); break;
|
||
case STEP_USER: draw_user(s, xd); break;
|
||
case STEP_REVIEW: draw_review(s, xd); break;
|
||
case STEP_INSTALL: draw_install(s, xd); break;
|
||
case STEP_DONE: draw_done(s, xd); break;
|
||
}
|
||
|
||
/* outer border */
|
||
XSetForeground(s->dpy, s->gc, mkcolor(s, C_BORDER));
|
||
XDrawRectangle(s->dpy, s->win, s->gc, 0, 0, WIN_W-1, WIN_H-1);
|
||
|
||
XftDrawDestroy(xd);
|
||
XFlush(s->dpy);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Installation runner */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void start_install(Ins *s) {
|
||
int pipefd[2];
|
||
if (pipe(pipefd) < 0) {
|
||
snprintf(s->install_err, sizeof(s->install_err),
|
||
"pipe() failed: %s", strerror(errno));
|
||
s->install_done = 1;
|
||
return;
|
||
}
|
||
|
||
pid_t pid = fork();
|
||
if (pid < 0) {
|
||
snprintf(s->install_err, sizeof(s->install_err),
|
||
"fork() failed: %s", strerror(errno));
|
||
s->install_done = 1;
|
||
return;
|
||
}
|
||
|
||
if (pid == 0) {
|
||
/* child */
|
||
close(pipefd[0]);
|
||
dup2(pipefd[1], STDOUT_FILENO);
|
||
dup2(pipefd[1], STDERR_FILENO);
|
||
close(pipefd[1]);
|
||
execl("/usr/local/bin/orioninstall", "orioninstall",
|
||
"--gui",
|
||
s->disks[s->sel_disk].path,
|
||
s->tf_host.buf,
|
||
s->tf_user.buf,
|
||
s->tf_pass.buf,
|
||
NULL);
|
||
fprintf(stderr, "exec orioninstall failed: %s\n", strerror(errno));
|
||
_exit(1);
|
||
}
|
||
|
||
/* parent */
|
||
close(pipefd[1]);
|
||
s->install_fd = pipefd[0];
|
||
/* non-blocking reads */
|
||
fcntl(s->install_fd, F_SETFL, O_NONBLOCK);
|
||
}
|
||
|
||
static void poll_install(Ins *s) {
|
||
if (s->install_fd < 0) return;
|
||
|
||
char buf[512];
|
||
static char linebuf[LOG_COLS + 4];
|
||
static int linelen = 0;
|
||
|
||
ssize_t n = read(s->install_fd, buf, sizeof(buf) - 1);
|
||
if (n > 0) {
|
||
buf[n] = '\0';
|
||
for (int i = 0; i < (int)n; i++) {
|
||
char c = buf[i];
|
||
if (c == '\n' || c == '\r') {
|
||
linebuf[linelen] = '\0';
|
||
if (linelen > 0) {
|
||
/* parse PROGRESS:pct:msg */
|
||
if (strncmp(linebuf, "PROGRESS:", 9) == 0) {
|
||
int pct = 0;
|
||
char msg[128] = {0};
|
||
sscanf(linebuf + 9, "%d:%127[^\n]", &pct, msg);
|
||
s->progress = pct;
|
||
snprintf(s->prog_msg, sizeof(s->prog_msg), "%s", msg);
|
||
if (pct == 100) {
|
||
s->install_done = 1;
|
||
s->step = STEP_DONE;
|
||
}
|
||
} else {
|
||
log_push(&s->log, linebuf);
|
||
}
|
||
redraw(s);
|
||
}
|
||
linelen = 0;
|
||
} else {
|
||
if (linelen < LOG_COLS)
|
||
linebuf[linelen++] = c;
|
||
}
|
||
}
|
||
} else if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||
/* EOF */
|
||
close(s->install_fd);
|
||
s->install_fd = -1;
|
||
if (!s->install_done) {
|
||
snprintf(s->install_err, sizeof(s->install_err),
|
||
"Installer exited unexpectedly.");
|
||
s->install_done = 1;
|
||
}
|
||
redraw(s);
|
||
}
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Text field input handling */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void tf_key(TF *tf, XKeyEvent *e) {
|
||
KeySym sym; char buf[8]; int len;
|
||
len = XLookupString(e, buf, sizeof(buf)-1, &sym, NULL);
|
||
buf[len] = '\0';
|
||
|
||
if (sym == XK_BackSpace) {
|
||
if (tf->len > 0) tf->buf[--tf->len] = '\0';
|
||
} else if (len > 0 && buf[0] >= 0x20 && buf[0] < 0x7F &&
|
||
tf->len < TF_MAX - 1) {
|
||
tf->buf[tf->len++] = buf[0];
|
||
tf->buf[tf->len] = '\0';
|
||
}
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Event handling */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static TF *tf_at(Ins *s, int x, int y) {
|
||
int fx = 200, fw = WIN_W - fx - 30, lh = 48;
|
||
int ys[] = { BODY_Y+24, BODY_Y+72, BODY_Y+120, BODY_Y+168 };
|
||
TF *tfs[] = { &s->tf_host, &s->tf_user, &s->tf_pass, &s->tf_pass2 };
|
||
for (int i = 0; i < 4; i++)
|
||
if (in_rect(x, y, fx, ys[i], fw, 32)) return tfs[i];
|
||
(void)lh;
|
||
return NULL;
|
||
}
|
||
|
||
static void handle_click(Ins *s, int x, int y) {
|
||
int bx, by, bw, bh;
|
||
|
||
/* next button */
|
||
next_rect(&bx, &by, &bw, &bh);
|
||
if (in_rect(x, y, bx, by, bw, bh)) {
|
||
if (!validate_step(s)) { redraw(s); return; }
|
||
switch (s->step) {
|
||
case STEP_WELCOME:
|
||
s->step = STEP_DISK;
|
||
if (s->ndisks == 0) detect_disks(s);
|
||
break;
|
||
case STEP_DISK: s->step = STEP_USER; break;
|
||
case STEP_USER: s->step = STEP_REVIEW; break;
|
||
case STEP_REVIEW:
|
||
s->step = STEP_INSTALL;
|
||
redraw(s);
|
||
start_install(s);
|
||
return;
|
||
case STEP_INSTALL:
|
||
if (s->install_done) {
|
||
system("reboot");
|
||
}
|
||
break;
|
||
case STEP_DONE:
|
||
system("reboot");
|
||
break;
|
||
}
|
||
redraw(s);
|
||
return;
|
||
}
|
||
|
||
/* back button */
|
||
back_rect(&bx, &by, &bw, &bh);
|
||
if (in_rect(x, y, bx, by, bw, bh) &&
|
||
s->step > STEP_WELCOME && s->step < STEP_INSTALL) {
|
||
s->step = (Step)((int)s->step - 1);
|
||
s->err_msg[0] = '\0';
|
||
redraw(s);
|
||
return;
|
||
}
|
||
|
||
/* disk list */
|
||
if (s->step == STEP_DISK) {
|
||
int dy = BODY_Y + 42;
|
||
for (int i = 0; i < s->ndisks; i++) {
|
||
if (in_rect(x, y, 30, dy, WIN_W-60, 44)) {
|
||
s->sel_disk = i;
|
||
redraw(s);
|
||
return;
|
||
}
|
||
dy += 46;
|
||
}
|
||
}
|
||
|
||
/* text fields */
|
||
if (s->step == STEP_USER) {
|
||
TF *hit = tf_at(s, x, y);
|
||
if (s->focused_tf) s->focused_tf->focused = 0;
|
||
s->focused_tf = hit;
|
||
if (hit) hit->focused = 1;
|
||
redraw(s);
|
||
}
|
||
}
|
||
|
||
static void handle_key(Ins *s, XKeyEvent *e) {
|
||
KeySym sym = XLookupKeysym(e, 0);
|
||
|
||
/* Tab cycles text fields */
|
||
if (sym == XK_Tab && s->step == STEP_USER) {
|
||
TF *order[] = { &s->tf_host, &s->tf_user, &s->tf_pass, &s->tf_pass2 };
|
||
int cur = -1;
|
||
for (int i = 0; i < 4; i++)
|
||
if (order[i] == s->focused_tf) { cur = i; break; }
|
||
if (s->focused_tf) s->focused_tf->focused = 0;
|
||
cur = (cur + 1) % 4;
|
||
s->focused_tf = order[cur];
|
||
s->focused_tf->focused = 1;
|
||
redraw(s);
|
||
return;
|
||
}
|
||
|
||
/* Enter advances */
|
||
if (sym == XK_Return) {
|
||
int bx, by, bw, bh;
|
||
next_rect(&bx, &by, &bw, &bh);
|
||
handle_click(s, bx + 1, by + 1);
|
||
return;
|
||
}
|
||
|
||
if (s->focused_tf) {
|
||
tf_key(s->focused_tf, e);
|
||
redraw(s);
|
||
}
|
||
}
|
||
|
||
static void handle_motion(Ins *s, int x, int y) {
|
||
int bx, by, bw, bh;
|
||
int prev_back = s->hov_back, prev_next = s->hov_next;
|
||
|
||
next_rect(&bx, &by, &bw, &bh);
|
||
s->hov_next = in_rect(x, y, bx, by, bw, bh);
|
||
|
||
back_rect(&bx, &by, &bw, &bh);
|
||
s->hov_back = in_rect(x, y, bx, by, bw, bh) &&
|
||
s->step > STEP_WELCOME && s->step < STEP_INSTALL;
|
||
|
||
if (s->hov_back != prev_back || s->hov_next != prev_next)
|
||
redraw(s);
|
||
}
|
||
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
/* Init & main loop */
|
||
/* ══════════════════════════════════════════════════════════════════ */
|
||
|
||
static void init(Ins *s) {
|
||
s->dpy = XOpenDisplay(NULL);
|
||
if (!s->dpy) { fputs("Cannot open display\n", stderr); exit(1); }
|
||
|
||
s->scr = DefaultScreen(s->dpy);
|
||
s->vis = DefaultVisual(s->dpy, s->scr);
|
||
s->cmap = DefaultColormap(s->dpy, s->scr);
|
||
s->depth = DefaultDepth(s->dpy, s->scr);
|
||
|
||
int sw = DisplayWidth(s->dpy, s->scr);
|
||
int sh = DisplayHeight(s->dpy, s->scr);
|
||
|
||
XSetWindowAttributes swa;
|
||
swa.background_pixel = mkcolor(s, C_BG);
|
||
swa.event_mask = ExposureMask | ButtonPressMask | PointerMotionMask
|
||
| KeyPressMask | StructureNotifyMask;
|
||
|
||
s->win = XCreateWindow(
|
||
s->dpy, RootWindow(s->dpy, s->scr),
|
||
(sw - WIN_W)/2, (sh - WIN_H)/2, WIN_W, WIN_H,
|
||
0, s->depth, InputOutput, s->vis,
|
||
CWBackPixel | CWEventMask, &swa);
|
||
s->gc = XCreateGC(s->dpy, s->win, 0, NULL);
|
||
|
||
/* window title + WM hints */
|
||
XStoreName(s->dpy, s->win, "OrionOS Installer");
|
||
Atom wm_del = XInternAtom(s->dpy, "WM_DELETE_WINDOW", False);
|
||
XSetWMProtocols(s->dpy, s->win, &wm_del, 1);
|
||
/* fixed size */
|
||
XSizeHints *sh2 = XAllocSizeHints();
|
||
sh2->flags = PMinSize | PMaxSize;
|
||
sh2->min_width = sh2->max_width = WIN_W;
|
||
sh2->min_height = sh2->max_height = WIN_H;
|
||
XSetWMNormalHints(s->dpy, s->win, sh2);
|
||
XFree(sh2);
|
||
|
||
/* fonts */
|
||
s->f_logo = XftFontOpenName(s->dpy, s->scr, F_LOGO);
|
||
s->f_head = XftFontOpenName(s->dpy, s->scr, F_HEAD);
|
||
s->f_body = XftFontOpenName(s->dpy, s->scr, F_BODY);
|
||
s->f_small = XftFontOpenName(s->dpy, s->scr, F_SMALL);
|
||
s->f_mono = XftFontOpenName(s->dpy, s->scr, F_MONO);
|
||
s->f_btn = XftFontOpenName(s->dpy, s->scr, F_BTN);
|
||
XftFont **ff[] = {&s->f_logo,&s->f_head,&s->f_body,
|
||
&s->f_small,&s->f_mono,&s->f_btn};
|
||
for (int i = 0; i < 6; i++)
|
||
if (!*ff[i])
|
||
*ff[i] = XftFontOpenName(s->dpy, s->scr, "fixed:size=10");
|
||
|
||
/* defaults */
|
||
s->step = STEP_WELCOME;
|
||
s->sel_disk = -1;
|
||
s->install_fd = -1;
|
||
s->progress = 0;
|
||
snprintf(s->prog_msg, sizeof(s->prog_msg), "Starting…");
|
||
snprintf(s->tf_host.buf, TF_MAX, "orionos");
|
||
s->tf_host.len = strlen("orionos");
|
||
snprintf(s->tf_user.buf, TF_MAX, "orion");
|
||
s->tf_user.len = strlen("orion");
|
||
s->tf_pass.password = 1;
|
||
s->tf_pass2.password = 1;
|
||
|
||
detect_disks(s);
|
||
if (s->ndisks == 1) s->sel_disk = 0;
|
||
|
||
XMapRaised(s->dpy, s->win);
|
||
XSync(s->dpy, False);
|
||
}
|
||
|
||
int main(void) {
|
||
Ins s = {0};
|
||
init(&s);
|
||
redraw(&s);
|
||
|
||
Atom wm_del = XInternAtom(s.dpy, "WM_DELETE_WINDOW", False);
|
||
|
||
for (;;) {
|
||
/* poll for X events or installer output */
|
||
int xfd = ConnectionNumber(s.dpy);
|
||
struct pollfd pfds[2];
|
||
pfds[0].fd = xfd; pfds[0].events = POLLIN;
|
||
pfds[1].fd = s.install_fd;
|
||
pfds[1].events = (s.install_fd >= 0) ? POLLIN : 0;
|
||
|
||
int r = poll(pfds, (s.install_fd >= 0) ? 2 : 1, 100);
|
||
(void)r;
|
||
|
||
/* drain installer output */
|
||
if (s.install_fd >= 0 && (pfds[1].revents & (POLLIN|POLLHUP)))
|
||
poll_install(&s);
|
||
/* also poll on timeout to catch buffered data */
|
||
if (s.install_fd >= 0 && r == 0)
|
||
poll_install(&s);
|
||
|
||
/* X events */
|
||
while (XPending(s.dpy)) {
|
||
XEvent e;
|
||
XNextEvent(s.dpy, &e);
|
||
switch (e.type) {
|
||
case Expose:
|
||
if (e.xexpose.count == 0) redraw(&s);
|
||
break;
|
||
case ButtonPress:
|
||
handle_click(&s, e.xbutton.x, e.xbutton.y);
|
||
break;
|
||
case MotionNotify:
|
||
handle_motion(&s, e.xmotion.x, e.xmotion.y);
|
||
break;
|
||
case KeyPress:
|
||
handle_key(&s, &e.xkey);
|
||
break;
|
||
case ClientMessage:
|
||
if ((Atom)e.xclient.data.l[0] == wm_del) return 0;
|
||
break;
|
||
default: break;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|