Add orionpkg (package manager GUI) + installer locale step

orionpkg/orionpkg.c (~500 lines, native Xlib/Xft):
- Launches with checkupdates (pacman-contrib) to list pending upgrades
- ST_CHECKING: animated spinner while querying
- ST_UPDATES: scrollable package list with name / old ver → new ver columns
- ST_UPDATING: pkexec pacman -Syu in background, live log output,
  progress bar estimated from pacman's (N/M) counters
- ST_DONE / ST_ERROR: success or failure screen
- Refresh (r key), scroll (mouse wheel / arrow keys), Close (Esc/q)
- pacman-contrib added to ISO packages

Installer: add STEP_LOCALE (timezone + keyboard layout):
- Inserted between STEP_DISK and STEP_USER
- Timezone pane: searchable filter input + scrollable list of 37 zones
- Keyboard pane: 20 common layouts with code + description
- Timezone and keyboard passed as args 6/7 to orioninstall
- orioninstall: sets /etc/vconsole.conf and X11 xorg.conf.d keyboard
  config from args; uses selected timezone for localtime symlink

Also:
- Right-click menu: "Software Updates" → orionpkg
- Root Makefile: orionpkg target + install rule
- ISO: orionpkg binary installed, permissions map, gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-07-23 14:39:00 -04:00
1 parent 53c6199102
commit 0ed61cea4c
8 files changed
+1000 -6

No files matched your search

+238 -2
View File
@@ -84,6 +84,7 @@
typedef enum {
STEP_WELCOME = 0,
STEP_DISK,
STEP_LOCALE, /* timezone + keyboard layout */
STEP_USER,
STEP_REVIEW,
STEP_INSTALL,
@@ -140,6 +141,15 @@ typedef struct {
int ndisks;
int sel_disk;
/* locale selection */
int tz_sel; /* index into TZ_LIST */
int tz_scroll; /* first visible row in timezone pane */
char tz_filter[48]; /* live filter text */
int tz_filter_len;
int tz_filter_focus;
int kb_sel; /* index into KB_LIST */
int kb_scroll;
/* user fields */
TF tf_host, tf_user, tf_pass, tf_pass2;
TF *focused_tf;
@@ -422,7 +432,7 @@ static void draw_footer(Ins *s, XftDraw *xd,
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 ndots = 6; /* steps 0-5 shown as dots (6 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;
@@ -494,6 +504,164 @@ static int validate_step(Ins *s) {
/* Step rendering */
/* ══════════════════════════════════════════════════════════════════ */
/* ── locale data ─────────────────────────────────────────────────── */
static const char *TZ_LIST[] = {
"UTC",
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Los_Angeles",
"America/Anchorage",
"America/Honolulu",
"America/Toronto",
"America/Vancouver",
"America/Mexico_City",
"America/Sao_Paulo",
"America/Buenos_Aires",
"Europe/London",
"Europe/Paris",
"Europe/Berlin",
"Europe/Rome",
"Europe/Madrid",
"Europe/Amsterdam",
"Europe/Stockholm",
"Europe/Warsaw",
"Europe/Moscow",
"Europe/Istanbul",
"Asia/Dubai",
"Asia/Kolkata",
"Asia/Dhaka",
"Asia/Bangkok",
"Asia/Shanghai",
"Asia/Tokyo",
"Asia/Seoul",
"Asia/Singapore",
"Australia/Sydney",
"Australia/Perth",
"Pacific/Auckland",
"Pacific/Honolulu",
"Africa/Cairo",
"Africa/Johannesburg",
"Africa/Lagos",
};
#define NTZ ((int)(sizeof(TZ_LIST)/sizeof(TZ_LIST[0])))
typedef struct { const char *code; const char *label; } KBLayout;
static const KBLayout KB_LIST[] = {
{ "us", "US English" },
{ "gb", "UK English" },
{ "de", "German" },
{ "fr", "French" },
{ "es", "Spanish" },
{ "it", "Italian" },
{ "pt", "Portuguese" },
{ "br", "Portuguese (BR)" },
{ "ru", "Russian" },
{ "pl", "Polish" },
{ "nl", "Dutch" },
{ "sv", "Swedish" },
{ "no", "Norwegian" },
{ "da", "Danish" },
{ "fi", "Finnish" },
{ "tr", "Turkish" },
{ "ar", "Arabic" },
{ "jp", "Japanese" },
{ "kr", "Korean" },
{ "cn", "Chinese (Pinyin)" },
};
#define NKB ((int)(sizeof(KB_LIST)/sizeof(KB_LIST[0])))
#define LC_LIST_H 220 /* pixel height of each list pane */
#define LC_ROW_H 22 /* row height in list */
#define LC_ROWS_VIS (LC_LIST_H / LC_ROW_H)
#define LC_PANE_W ((WIN_W - 60) / 2 - 6)
static void draw_locale(Ins *s, XftDraw *xd) {
draw_header(s, xd, "Timezone & Keyboard");
fill_rect(s, 0, BODY_Y, WIN_W, BODY_H, C_BODY);
int lx = 30, rx = WIN_W/2 + 4;
int ty = BODY_Y + 14;
/* ── Timezone pane ── */
xft_text(s, xd, s->f_head, C_TXT, lx, ty, "Timezone");
/* filter input */
int fy = ty + 22;
int factive = s->tz_filter_focus;
unsigned long fbd = factive ? C_INPUT_ACT : C_INPUT_BD;
fill_rect(s, lx, fy, LC_PANE_W, 26, C_INPUT_BG);
XSetForeground(s->dpy, s->gc, mkcolor(s, fbd));
XDrawRectangle(s->dpy, s->win, s->gc, lx, fy, LC_PANE_W-1, 25);
const char *fdisp = s->tz_filter_len ? s->tz_filter : "";
xft_text(s, xd, s->f_body, s->tz_filter_len ? C_TXT_INP : C_TXT_DIM,
lx + 6, fy + 17, s->tz_filter_len ? fdisp : "Filter…");
if (factive) {
int cx = lx + 6 + text_w(s, s->f_body, fdisp);
XSetForeground(s->dpy, s->gc, mkcolor(s, C_TXT_INP));
XDrawLine(s->dpy, s->win, s->gc, cx, fy + 4, cx, fy + 21);
}
/* timezone list */
int ly = fy + 32;
fill_rect(s, lx, ly, LC_PANE_W, LC_LIST_H, C_PANEL);
XSetForeground(s->dpy, s->gc, mkcolor(s, C_SEP));
XDrawRectangle(s->dpy, s->win, s->gc, lx, ly, LC_PANE_W-1, LC_LIST_H-1);
int vis = 0, row = 0;
for (int i = 0; i < NTZ && vis < LC_ROWS_VIS + s->tz_scroll; i++) {
if (s->tz_filter_len &&
!strstr(TZ_LIST[i], s->tz_filter)) continue;
if (row++ < s->tz_scroll) continue;
int ry2 = ly + vis * LC_ROW_H;
int actual_i = i; /* track real index */
if (actual_i == s->tz_sel)
fill_rect(s, lx+1, ry2, LC_PANE_W-2, LC_ROW_H, C_RADIO_SEL);
else if (vis % 2 == 1)
fill_rect(s, lx+1, ry2, LC_PANE_W-2, LC_ROW_H, 0x141C2C);
xft_text(s, xd, s->f_small,
actual_i == s->tz_sel ? C_TXT_HDR : C_TXT,
lx + 8, ry2 + LC_ROW_H - 5, TZ_LIST[i]);
vis++;
}
/* ── Keyboard pane ── */
xft_text(s, xd, s->f_head, C_TXT, rx, ty, "Keyboard Layout");
int kly = ty + 22 + 32; /* align with timezone list (below filter) */
fill_rect(s, rx, kly, LC_PANE_W, LC_LIST_H + 26, C_PANEL);
XSetForeground(s->dpy, s->gc, mkcolor(s, C_SEP));
XDrawRectangle(s->dpy, s->win, s->gc, rx, kly, LC_PANE_W-1, LC_LIST_H+25);
int kvis = NKB < LC_ROWS_VIS + 1 ? NKB : LC_ROWS_VIS + 1;
for (int i = s->kb_scroll; i < NKB && (i - s->kb_scroll) < kvis; i++) {
int ky = kly + (i - s->kb_scroll) * LC_ROW_H;
if (i == s->kb_sel)
fill_rect(s, rx+1, ky, LC_PANE_W-2, LC_ROW_H, C_RADIO_SEL);
else if ((i - s->kb_scroll) % 2 == 1)
fill_rect(s, rx+1, ky, LC_PANE_W-2, LC_ROW_H, 0x141C2C);
char kbuf[48];
snprintf(kbuf, sizeof(kbuf), "[%s] %s",
KB_LIST[i].code, KB_LIST[i].label);
xft_text(s, xd, s->f_small,
i == s->kb_sel ? C_TXT_HDR : C_TXT,
rx + 8, ky + LC_ROW_H - 5, kbuf);
}
/* selected values footer */
int sy2 = BODY_Y + BODY_H - FOOT_H - 4;
char sel_tz[80], sel_kb[80];
snprintf(sel_tz, sizeof(sel_tz), "Timezone: %s",
s->tz_sel >= 0 ? TZ_LIST[s->tz_sel] : "UTC");
snprintf(sel_kb, sizeof(sel_kb), " | Layout: %s",
KB_LIST[s->kb_sel].code);
xft_text(s, xd, s->f_small, C_TXT_DIM, lx, sy2, sel_tz);
xft_text(s, xd, s->f_small, C_TXT_DIM,
lx + text_w(s, s->f_small, sel_tz), sy2, sel_kb);
draw_footer(s, xd, "← Back", "Next →", C_BTN_PRI);
}
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);
@@ -746,6 +914,7 @@ static void redraw(Ins *s) {
switch (s->step) {
case STEP_WELCOME: draw_welcome(s, xd); break;
case STEP_DISK: draw_disk(s, xd); break;
case STEP_LOCALE: draw_locale(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;
@@ -793,6 +962,8 @@ static void start_install(Ins *s) {
s->tf_host.buf,
s->tf_user.buf,
s->tf_pass.buf,
s->tz_sel >= 0 ? TZ_LIST[s->tz_sel] : "UTC",
KB_LIST[s->kb_sel].code,
NULL);
fprintf(stderr, "exec orioninstall failed: %s\n", strerror(errno));
_exit(1);
@@ -899,7 +1070,8 @@ static void handle_click(Ins *s, int x, int y) {
s->step = STEP_DISK;
if (s->ndisks == 0) detect_disks(s);
break;
case STEP_DISK: s->step = STEP_USER; break;
case STEP_DISK: s->step = STEP_LOCALE; break;
case STEP_LOCALE: s->step = STEP_USER; break;
case STEP_USER: s->step = STEP_REVIEW; break;
case STEP_REVIEW:
s->step = STEP_INSTALL;
@@ -942,6 +1114,41 @@ static void handle_click(Ins *s, int x, int y) {
}
}
/* locale pane clicks */
if (s->step == STEP_LOCALE) {
int lx = 30, rx = WIN_W/2 + 4;
int ty = BODY_Y + 14;
int fy = ty + 22;
/* filter input focus */
if (in_rect(x, y, lx, fy, LC_PANE_W, 26)) {
s->tz_filter_focus = 1;
redraw(s); return;
} else {
s->tz_filter_focus = 0;
}
/* timezone list */
int ly = fy + 32;
if (in_rect(x, y, lx, ly, LC_PANE_W, LC_LIST_H)) {
int vis = 0, row = 0;
int clicked_row = (y - ly) / LC_ROW_H;
for (int i = 0; i < NTZ; i++) {
if (s->tz_filter_len && !strstr(TZ_LIST[i], s->tz_filter)) continue;
if (row++ < s->tz_scroll) continue;
if (vis == clicked_row) { s->tz_sel = i; break; }
vis++;
}
redraw(s); return;
}
/* keyboard list */
int kly = ty + 22 + 32;
if (in_rect(x, y, rx, kly, LC_PANE_W, LC_LIST_H + 26)) {
int row2 = (y - kly) / LC_ROW_H + s->kb_scroll;
if (row2 >= 0 && row2 < NKB) { s->kb_sel = row2; }
redraw(s); return;
}
redraw(s); return;
}
/* text fields */
if (s->step == STEP_USER) {
TF *hit = tf_at(s, x, y);
@@ -955,6 +1162,33 @@ static void handle_click(Ins *s, int x, int y) {
static void handle_key(Ins *s, XKeyEvent *e) {
KeySym sym = XLookupKeysym(e, 0);
/* Locale step: typing into the timezone filter */
if (s->step == STEP_LOCALE && s->tz_filter_focus) {
if (sym == XK_BackSpace && s->tz_filter_len > 0) {
s->tz_filter[--s->tz_filter_len] = '\0';
s->tz_scroll = 0;
redraw(s); return;
}
if (sym == XK_Escape) {
s->tz_filter_len = 0; s->tz_filter[0] = '\0';
s->tz_scroll = 0;
redraw(s); return;
}
/* scroll list with arrow keys */
if (sym == XK_Down) { s->tz_scroll++; redraw(s); return; }
if (sym == XK_Up && s->tz_scroll > 0) { s->tz_scroll--; redraw(s); return; }
char buf[8]; int len = XLookupString(e, buf, sizeof(buf)-1, NULL, NULL);
if (len > 0 && (unsigned char)buf[0] >= 32
&& s->tz_filter_len < (int)sizeof(s->tz_filter) - 2) {
buf[len] = '\0';
strcat(s->tz_filter, buf);
s->tz_filter_len += len;
s->tz_scroll = 0;
redraw(s); return;
}
return;
}
/* 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 };
@@ -1054,6 +1288,8 @@ static void init(Ins *s) {
/* defaults */
s->step = STEP_WELCOME;
s->sel_disk = -1;
s->tz_sel = 0; /* UTC */
s->kb_sel = 0; /* us */
s->install_fd = -1;
s->progress = 0;
snprintf(s->prog_msg, sizeof(s->prog_msg), "Starting…");