Compare commits

...
15 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 4.6 0fdc14e26d Wire up installer, networking, and fix UEFI boot entry
- Dockerfile: compile and install orioninstall-gui + orioninstall script
- packages: add rsync, parted, arch-install-scripts, grub, efibootmgr,
  dosfstools, amd/intel-ucode, wpa_supplicant
- customize_airootfs.sh: enable NetworkManager + wpa_supplicant on boot
- orioninstall: fix UEFI (ESP at /boot, not /boot/efi); pass TIMEZONE
  and KEYBOARD into chroot heredoc; conditionalize microcode initrd lines
- menu: launch orioninstall-gui via pkexec (runs as root)
- profiledef: add file_permissions for installer binaries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 13:47:25 -04:00
nmemmertandClaude Sonnet 4.6 333eb6bc3e Add mirrorlist and pacman.conf to live airootfs
Without these the live system has no mirrors configured and pacman
refuses to sync. Uses geo.mirror.pkgbuild.com as primary with
kernel.org, rackspace, and MIT as fallbacks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 13:40:07 -04:00
nmemmertandClaude Sonnet 4.6 4854ab85aa Redesign launcher: modern dark UI, 4-col grid, pill filters, footer
- 640x490 window, larger cards (132x106), 4 columns
- Full-width search bar in header with active-state indicator
- Category filters as pill chips with gaps
- Cards show surface bg + accent top bar on hover
- Footer bar with app count and keyboard hints
- Fix menu: pavucontrol -> alsamixer in xterm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 13:35:23 -04:00
nmemmertandClaude Sonnet 4.6 daf9cb4f96 Fix menu/launcher to use installed apps; add pcmanfm, mousepad, arandr
Menu and launcher fallback pointed to alacritty/firefox/rigel/quill
which are not in the live ISO. Switch to xterm (already installed),
pcmanfm and mousepad (now added to packages). Also add arandr for
display settings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 13:29:45 -04:00
nmemmertandClaude Sonnet 4.6 1fa8f212b1 Auto-start X on boot: autologin root on TTY1, startx from .bash_profile
Also add all OrionOS binaries to file_permissions so archiso sets 755
on them in the squashfs (fixes Permission denied from xinitrc).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 13:20:03 -04:00
nmemmertandClaude Sonnet 4.6 689249cf5e Add customize_airootfs.sh to remove root password on live system
Without this, pacstrap locks root's password by default and the live
ISO is unbootable/unloggable-into.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 12:55:53 -04:00
nmemmertandClaude Sonnet 4.6 5de7cfec78 archiso hook: replace getarg with direct /proc/cmdline parsing
getarg's trailing-= convention varies by mkinitcpio version and was
returning empty even when archisosearchuuid was present in cmdline.
Parse /proc/cmdline directly in ash — no external tools, no version
sensitivity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 12:39:06 -04:00
nmemmertandClaude Sonnet 4.6 685b725dbb archiso hook: support archisolabel= fallback for syslinux boots
Syslinux passes archisolabel= not archisosearchuuid= (UUID is only in
systemd-boot entries). Add blkid label lookup as fallback so both boot
paths work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 12:32:27 -04:00
nmemmertandClaude Sonnet 4.6 9159626cff Fix archiso initcpio hook: use run_hook+mount_handler override
run_latehook fires after mkinitcpio already attempts to mount root= (empty),
which fails. Switch to run_hook() setting mount_handler=archiso_mount_handler
so mkinitcpio calls our handler instead of the default block-device mount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 12:05:41 -04:00
nmemmertandClaude Sonnet 4.6 ca0e9f7c72 Bundle archiso initcpio hook directly in repo
Current archiso package does not install initcpio hook files (confirmed:
find /usr -path */initcpio/hooks/archiso returns nothing). Write the hook
and its mkinitcpio install script directly into the profile airootfs so
COPY carries them into the image without any package dependency.

Hook finds ISO by archisosearchuuid= kernel param, waits up to 30s for
the device to appear via blkid, mounts iso9660→squashfs→overlay onto
/new_root. mkinitcpio detects /new_root is already mounted and succeeds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 11:34:08 -04:00
nmemmertandClaude Sonnet 4.6 a3beffae15 Find archiso initcpio hooks by path rather than hardcoded location
Current archiso package version may place hooks at a non-standard path.
Use find to locate hooks/archiso and install/archiso anywhere under /usr
and copy to the canonical initcpio location in airootfs.
Prints found paths so build output shows where they came from.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 11:14:05 -04:00
nmemmertandClaude Sonnet 4.6 ebb61beaac Copy archiso initcpio hooks directly into airootfs at build time
The hook package install inside the pacstrap chroot may silently skip
the initcpio install step under QEMU emulation, leaving the archiso hook
absent from the initrd (confirmed: no 'running late hook [archiso]' in
boot output despite UUID match on device).

Explicitly copy /usr/lib/initcpio/hooks/archiso and install/archiso from
the build host into the airootfs before mkarchiso runs mkinitcpio, so
the hook is present regardless of how the chroot package install behaves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 10:52:05 -04:00
nmemmertandClaude Sonnet 4.6 525bb49787 Fix live boot: custom syslinux.cfg and explicit squashfs/overlay modules
The releng syslinux.cfg references UI .c32 modules that may not load,
causing syslinux to fall back without archisolabel= in the kernel cmdline
so the archiso initrd hook scans for an empty device label and sets root=''.

- Write our own minimal syslinux.cfg using %INSTALL_DIR%/%ARCHISO_LABEL%
  placeholders; Dockerfile now only copies .c32 binaries from releng
- Add squashfs and overlay to mkinitcpio MODULES explicitly
- Remove microcode hook (no intel-ucode/amd-ucode packages installed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 09:36:45 -04:00
nmemmertandClaude Sonnet 4.6 d6f2cbb2b8 Add archiso package to live ISO for working initrd hook
mkinitcpio runs inside the airootfs chroot, so the archiso hook files
(/usr/lib/initcpio/hooks/archiso) must be installed there. Without this
the hook has no device-scanning logic and boots to an empty root mount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 09:11:12 -04:00
nmemmertandClaude Sonnet 4.6 18d3675368 Fix ISO build pipeline for Docker on macOS
- Disable pacman alpm sandbox in Dockerfile (seccomp blocks it in BuildKit)
- Move mkarchiso from RUN to CMD so it runs with --privileged at docker run time
- Fix output path: mkarchiso now writes to /dist (matches volume mount)
- Add syslinux + imlib2 to Docker build packages and live ISO package list
- Copy syslinux/ and efiboot/ boot configs from archiso releng into profile
- Switch deprecated UEFI grub bootmodes to uefi-x64.systemd-boot
- Fix wrong package names: libpam→pam, light→brightnessctl, remove pkexec
- Set SigLevel=Never and hardcode reliable mirrors in profile pacman.conf
- Add mkinitcpio.conf with archiso hook so initrd can mount the live squashfs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-31 08:27:16 -04:00
17 changed files with 380 additions and 152 deletions

No files matched your search

+1 -1
View File
@@ -66,7 +66,7 @@ docker run \
# ── rename with date tag ──────────────────────────────────────────────
echo ""
echo "Step 3/3: Finalizing..."
ISO="$(find "$DIST" -name "orionos*.iso" | head -1)"
ISO="$(find "$DIST" -iname "*.iso" | head -1)"
if [[ -z "$ISO" ]]; then
echo "ERROR: ISO not found in $DIST — check the build output above."
exit 1
+15 -15
View File
@@ -152,8 +152,8 @@ progress 14 "Mounting partitions…"
info "Mounting..."
mount "$ROOT_PART" /mnt
if [[ "$BOOT_MODE" == "uefi" ]]; then
mkdir -p /mnt/boot/efi
mount "$EFI_PART" /mnt/boot/efi
mkdir -p /mnt/boot
mount "$EFI_PART" /mnt/boot
fi
ok "Mounted."
@@ -197,9 +197,11 @@ info "Configuring system..."
arch-chroot /mnt /bin/bash -s "$HOSTNAME" "$USERNAME" "$PASSWORD" \
"$BOOT_MODE" "$ROOT_PART" \
"${TIMEZONE:-UTC}" "${KEYBOARD:-us}" \
<<'CHROOT'
HOSTNAME="$1"; USERNAME="$2"; PASSWORD="$3"
BOOT_MODE="$4"; ROOT_PART="$5"
TIMEZONE="${6:-UTC}"; KEYBOARD="${7:-us}"
# Hostname
echo "$HOSTNAME" > /etc/hostname
@@ -268,22 +270,20 @@ mkinitcpio -P
# Bootloader
if [[ "$BOOT_MODE" == "uefi" ]]; then
bootctl install 2>/dev/null || true
bootctl --esp-path=/boot install 2>/dev/null || true
ROOT_UUID="$(blkid -s UUID -o value "$ROOT_PART")"
mkdir -p /boot/efi/loader/entries
cat > /boot/efi/loader/loader.conf <<'LC'
default skyos.conf
mkdir -p /boot/loader/entries
cat > /boot/loader/loader.conf <<'LC'
default orionos.conf
timeout 5
editor no
LC
cat > /boot/efi/loader/entries/skyos.conf <<ENTRY
title OrionOS
linux /vmlinuz-linux
initrd /amd-ucode.img
initrd /intel-ucode.img
initrd /initramfs-linux.img
options root=UUID=${ROOT_UUID} rw quiet loglevel=3
ENTRY
# Build initrd lines — only include microcode if the image exists
INITRD_LINES="initrd /initramfs-linux.img"
[[ -f /boot/amd-ucode.img ]] && INITRD_LINES="initrd /amd-ucode.img\n${INITRD_LINES}"
[[ -f /boot/intel-ucode.img ]] && INITRD_LINES="initrd /intel-ucode.img\n${INITRD_LINES}"
printf "title OrionOS\nlinux /vmlinuz-linux\n%b\noptions root=UUID=%s rw quiet loglevel=3\n" \
"$INITRD_LINES" "$ROOT_UUID" > /boot/loader/entries/orionos.conf
else
# Find the whole disk (strip trailing partition number)
DISK="${ROOT_PART%[0-9]}"
@@ -305,7 +305,7 @@ ok "System configured."
progress 97 "Unmounting…"
info "Unmounting..."
sync
[[ "$BOOT_MODE" == "uefi" ]] && umount /mnt/boot/efi 2>/dev/null || true
[[ "$BOOT_MODE" == "uefi" ]] && umount /mnt/boot 2>/dev/null || true
umount /mnt
ok "Done."
+30 -7
View File
@@ -1,9 +1,13 @@
FROM archlinux:latest
# Disable pacman's alpm sandbox (seccomp-incompatible inside Docker/BuildKit)
RUN echo "DisableSandbox" >> /etc/pacman.conf
# Update and install archiso + OrionOS build dependencies
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm \
archiso \
syslinux \
base-devel \
git \
libx11 \
@@ -11,6 +15,7 @@ RUN pacman -Syu --noconfirm && \
libxfixes \
libxext \
libpng \
imlib2 \
dbus \
pam \
fontconfig \
@@ -30,8 +35,24 @@ WORKDIR /build
# Copy OrionOS source
COPY . /build/orionos
# Build OrionOS components
RUN cd /build/orionos && make 2>&1 | tail -20
# Bootstrap boot loader configs from the installed archiso releng profile.
# Copy only the .c32 binary modules — our repo provides syslinux.cfg.
# efiboot/ is needed for systemd-boot UEFI; fall back to a minimal loader.conf.
RUN mkdir -p /build/orionos/iso/profile/syslinux && \
cp /usr/share/archiso/configs/releng/syslinux/*.c32 \
/build/orionos/iso/profile/syslinux/ 2>/dev/null || true && \
if [ -d /usr/share/archiso/configs/releng/efiboot ]; then \
cp -r /usr/share/archiso/configs/releng/efiboot \
/build/orionos/iso/profile/; \
else \
mkdir -p /build/orionos/iso/profile/efiboot/loader/entries && \
printf 'timeout 4\ndefault arch-*\n' \
> /build/orionos/iso/profile/efiboot/loader/loader.conf; \
fi
# Build OrionOS components (full output — no tail so errors are visible)
RUN cd /build/orionos && make
# Install binaries into the staging root that archiso will include
RUN cd /build/orionos && \
@@ -44,6 +65,9 @@ RUN cd /build/orionos && \
install -Dm755 orionshot/orionshot /build/orionos/iso/profile/airootfs/usr/local/bin/orionshot && \
install -Dm755 orionclip/orionclip /build/orionos/iso/profile/airootfs/usr/local/bin/orionclip && \
install -Dm755 orionpkg/orionpkg /build/orionos/iso/profile/airootfs/usr/local/bin/orionpkg && \
install -Dm755 installer/orioninstall-gui /build/orionos/iso/profile/airootfs/usr/local/bin/orioninstall-gui && \
install -Dm755 installer/orioninstall /build/orionos/iso/profile/airootfs/usr/local/bin/orioninstall && \
mkdir -p /build/orionos/iso/profile/airootfs/usr/share/themes/ && \
cp -r themes/OrionOS /build/orionos/iso/profile/airootfs/usr/share/themes/ && \
install -Dm644 themes/picom.conf /build/orionos/iso/profile/airootfs/etc/orionos/picom.conf
@@ -53,10 +77,9 @@ RUN mkdir -p /build/orionos/iso/profile/airootfs/usr/share/orionos/wallpapers &&
cp -f /build/orionos/themes/wallpapers/orionos-default.png \
/build/orionos/iso/profile/airootfs/usr/share/orionos/wallpapers/ 2>/dev/null || true
# Build the ISO
RUN mkarchiso -v \
# Build the ISO at container runtime — mkarchiso needs --privileged (from docker run)
# Output goes to /dist which build-iso.sh mounts from the host.
CMD mkarchiso -v \
-w /tmp/iso-work \
-o /output \
-o /dist \
/build/orionos/iso/profile
CMD ["/bin/bash"]
+4
View File
@@ -0,0 +1,4 @@
MODULES=(squashfs overlay)
BINARIES=()
FILES=()
HOOKS=(base udev archiso block filesystems keyboard)
+16
View File
@@ -0,0 +1,16 @@
[options]
HoldPkg = pacman glibc
Architecture = auto
CheckSpace
ParallelDownloads = 5
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
[core]
Include = /etc/pacman.d/mirrorlist
[extra]
Include = /etc/pacman.d/mirrorlist
[multilib]
Include = /etc/pacman.d/mirrorlist
@@ -0,0 +1,4 @@
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch
Server = https://mirrors.mit.edu/archlinux/$repo/os/$arch
@@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I $TERM
+3
View File
@@ -0,0 +1,3 @@
if [[ -z $DISPLAY ]] && [[ $(tty) == /dev/tty1 ]]; then
exec startx
fi
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e -u
# Allow passwordless root login on the live system
passwd -d root
# Enable networking on the live system
systemctl enable NetworkManager
systemctl enable wpa_supplicant
@@ -0,0 +1,65 @@
#!/usr/bin/ash
archiso_mount_handler() {
local _new_root="$1"
local _uuid _label _basedir _dev _img _param
for _param in $(cat /proc/cmdline); do
case "$_param" in
archisosearchuuid=*) _uuid="${_param#archisosearchuuid=}" ;;
archisolabel=*) _label="${_param#archisolabel=}" ;;
archisobasedir=*) _basedir="${_param#archisobasedir=}" ;;
esac
done
: "${_basedir:=arch}"
if [ -z "${_uuid}" ] && [ -z "${_label}" ]; then
echo "archiso: neither archisosearchuuid nor archisolabel in kernel cmdline"
return 1
fi
local _waited=0
while [ "${_waited}" -lt 30 ]; do
if [ -n "${_uuid}" ]; then
_dev="$(blkid -l -o device -t UUID="${_uuid}" 2>/dev/null)"
else
_dev="$(blkid -l -o device -t LABEL="${_label}" 2>/dev/null)"
fi
[ -n "${_dev}" ] && break
sleep 1
_waited=$(( _waited + 1 ))
done
if [ -z "${_dev}" ]; then
echo "archiso: could not find boot medium (uuid=${_uuid} label=${_label})"
return 1
fi
echo "archiso: found boot medium ${_dev}"
mkdir -p /run/archiso/bootmnt
mount -r "${_dev}" /run/archiso/bootmnt || { echo "archiso: mount bootmnt failed"; return 1; }
_img="/run/archiso/bootmnt/${_basedir}/x86_64/airootfs.sfs"
if [ ! -e "${_img}" ]; then
echo "archiso: squashfs not found at ${_img}"
return 1
fi
mkdir -p /run/archiso/airootfs
mount -r -t squashfs "${_img}" /run/archiso/airootfs || { echo "archiso: squashfs mount failed"; return 1; }
mkdir -p /run/archiso/overlay
mount -t tmpfs tmpfs -o mode=0755 /run/archiso/overlay
mkdir -p /run/archiso/overlay/upper /run/archiso/overlay/work
mount -t overlay overlay \
-o lowerdir=/run/archiso/airootfs,upperdir=/run/archiso/overlay/upper,workdir=/run/archiso/overlay/work \
"${_new_root}" || { echo "archiso: overlay mount failed"; return 1; }
echo "archiso: live root mounted on ${_new_root}"
}
run_hook() {
mount_handler="archiso_mount_handler"
}
@@ -0,0 +1,19 @@
#!/bin/bash
# mkinitcpio install script for the archiso live-boot hook.
build() {
add_runscript
add_binary blkid
add_binary mount
add_binary sleep
add_module squashfs
add_module overlay
add_module isofs
}
help() {
cat <<HELPEOF
Locates the live ISO by UUID, mounts the squashfs airootfs, and sets up
a RAM-backed overlay as the writable root filesystem.
HELPEOF
}
+19 -3
View File
@@ -24,30 +24,45 @@ libxft
libxfixes
libxext
libpng
imlib2
dbus
libpam
pam
fontconfig
freetype2
syslinux
# Desktop utilities
feh
picom
xterm
libnotify
pcmanfm
mousepad
arandr
# Audio
alsa-utils
pulseaudio
# Brightness
light
brightnessctl
# Media playback control
playerctl
# Installer dependencies
rsync
parted
arch-install-scripts
grub
efibootmgr
dosfstools
amd-ucode
intel-ucode
# Arch tools
archiso
pacman-contrib
pkexec
polkit
# System tools
@@ -63,6 +78,7 @@ gzip
# Network
networkmanager
wpa_supplicant
dhcpcd
# Misc
+11 -4
View File
@@ -1,14 +1,21 @@
[options]
HoldPkg = pacman glibc
HoldPkg = pacman glibc
Architecture = auto
CheckSpace
ParallelDownloads = 5
SigLevel = Never
[core]
Include = /etc/pacman.d/mirrorlist
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch
[extra]
Include = /etc/pacman.d/mirrorlist
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch
[multilib]
Include = /etc/pacman.d/mirrorlist
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch
Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch
+14 -2
View File
@@ -9,8 +9,7 @@ iso_version="$(date +%Y.%m)"
install_dir="arch"
buildmodes=('iso')
bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito'
'uefi-ia32.grub.esp' 'uefi-x64.grub.esp'
'uefi-ia32.grub.eltorito' 'uefi-x64.grub.eltorito')
'uefi-x64.systemd-boot.esp' 'uefi-x64.systemd-boot.eltorito')
arch="x86_64"
pacman_conf="pacman.conf"
airootfs_image_type="squashfs"
@@ -18,5 +17,18 @@ airootfs_image_tool_options=('-comp' 'zstd' '-Xcompression-level' '15')
file_permissions=(
["/etc/orionos/xinitrc"]="0:0:755"
["/root/.xinitrc"]="0:0:755"
["/root/.bash_profile"]="0:0:644"
["/etc/skel/.xinitrc"]="0:0:755"
["/root/customize_airootfs.sh"]="0:0:755"
["/usr/local/bin/orionwm"]="0:0:755"
["/usr/local/bin/oriondeskbar"]="0:0:755"
["/usr/local/bin/orionlauncher"]="0:0:755"
["/usr/local/bin/orionwallpaper"]="0:0:755"
["/usr/local/bin/orionnotify"]="0:0:755"
["/usr/local/bin/orionlock"]="0:0:755"
["/usr/local/bin/orionshot"]="0:0:755"
["/usr/local/bin/orionclip"]="0:0:755"
["/usr/local/bin/orionpkg"]="0:0:755"
["/usr/local/bin/orioninstall"]="0:0:755"
["/usr/local/bin/orioninstall-gui"]="0:0:755"
)
+8
View File
@@ -0,0 +1,8 @@
DEFAULT arch64
TIMEOUT 50
PROMPT 0
LABEL arch64
LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux
INITRD /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img
APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% rw
+144 -104
View File
@@ -11,51 +11,55 @@
#include <unistd.h>
/* ── dimensions ─────────────────────────────────────────────────────── */
#define WIN_W 580
#define WIN_H 420
#define HDR_H 50
#define TAB_H 34
#define CONTENT_Y (HDR_H + TAB_H)
#define PAD 12
#define CARD_W 96
#define CARD_H 84
#define CARD_GAP 8
#define COLS 5
#define ICON_SZ 38
#define WIN_W 640
#define WIN_H 490
#define HDR_H 58
#define PILLS_H 42
#define CONTENT_Y (HDR_H + PILLS_H)
#define FOOTER_H 28
#define CARD_W 132
#define CARD_H 106
#define CARD_GAP 10
#define COLS 4
#define ICON_SZ 46
#define PILL_GAP 8
#define ROWS_VIS 3
#define GX0 ((WIN_W - (COLS * CARD_W + (COLS-1) * CARD_GAP)) / 2)
#define GY0 (CONTENT_Y + PAD)
#define ROWS_VIS 3 /* rows visible before scrolling */
#define GY0 (CONTENT_Y + 12)
#define MAX_APPS 256
/* ── palette ─────────────────────────────────────────────────────────── */
#define C_BG 0x0E1220
#define C_HDR 0x090C18
#define C_BORDER 0x192236
#define C_ACCENT 0x2258B0
#define C_TAB_SEL 0x141E30
#define C_CARD_HOV 0x131C2E
#define C_CARD_BD 0x1E2E48
#define C_SEARCH 0x131A28
#define C_SEARCH_BD 0x1C2C44
#define C_TEXT 0x8AAAC8
#define C_TEXT_HOV 0xE8F0FF
#define C_TEXT_DIM 0x344A62
#define C_TAB_TXT 0x486070
#define C_TAB_TSEL 0xA8C4E8
#define C_SCROLL_BG 0x0C1020
#define C_SCROLL_TH 0x1E3258
#define C_BG 0x111318
#define C_SURFACE 0x1A1D28
#define C_SURFACE_HV 0x1F2335
#define C_BORDER 0x252A42
#define C_BORDER_HV 0x3A5490
#define C_ACCENT 0x4B8FF0
#define C_PILL_SEL 0x19305C
#define C_PILL_BD 0x2A4480
#define C_SRCH_BG 0x161820
#define C_SRCH_BD 0x262C48
#define C_TEXT 0xC0CFF0
#define C_TEXT_HOV 0xEEF2FF
#define C_TEXT_DIM 0x3A4068
#define C_TEXT_CAT 0x586090
#define C_TEXT_CAT_S 0x8AAEE8
#define C_FOOTER_BG 0x0C0E14
#define C_FOOTER_TXT 0x353C62
#define C_SCROLL_BG 0x161820
#define C_SCROLL_TH 0x2C3460
static const unsigned long ICON_BG[] = {
0x1A4D88, 0x166C3C, 0x6C3018,
0x4A1A80, 0x126070, 0x6C1A38,
0x365218, 0x183460,
0x1A3A88, 0x0F6040, 0x7A2820, 0x501A80,
0x106070, 0x6A1A3A, 0x384C18, 0x1A3060,
0x6A4010, 0x103A60,
};
#define NICON 8
#define NICON 10
#define FONT_UI "Sans:size=9"
#define FONT_LOGO "Sans:size=11:weight=bold"
#define FONT_ICON "Sans:size=13:weight=bold"
#define FONT_ICON "Sans:size=14:weight=bold"
#define FONT_HINT "Sans:size=8"
#define MAX_FLT 64
typedef struct { char name[64]; char cmd[256]; char cat[32]; char icon[64]; } App;
@@ -171,18 +175,13 @@ static void load_apps(void) {
load_desktop_dir(p);
}
/* fallback if no .desktop files (dev environment, macOS, etc.) */
if (NAPP == 0) {
static const struct { const char *n, *c, *cat; } FB[] = {
{ "Firefox", "firefox &", "Internet" },
{ "Terminal", "alacritty &", "System" },
{ "Files", "thunar &", "System" },
{ "Text Editor", "mousepad &", "System" },
{ "Media", "mpv --player-operation-mode=pseudo-gui &", "Media" },
{ "Images", "viewnior &", "Media" },
{ "PDF Viewer", "evince &", "Office" },
{ "Display", "arandr &", "Settings" },
{ "Audio", "pavucontrol &", "Settings" },
{ "Terminal", "xterm &", "System" },
{ "Files", "pcmanfm &", "System" },
{ "Text Editor", "mousepad &", "System" },
{ "System Mon", "xterm -e htop &", "System" },
{ "Display", "arandr &", "Settings" },
};
for (int i = 0; i < (int)(sizeof(FB)/sizeof(FB[0])); i++) {
snprintf(APPS[i].name, 64, "%s", FB[i].n);
@@ -200,7 +199,7 @@ typedef struct {
Display *dpy; int screen;
Window root, win; GC gc;
Colormap cmap; Visual *visual;
XftFont *font_ui, *font_logo, *font_icon;
XftFont *font_ui, *font_icon, *font_hint;
int sel_cat, hov, scroll;
char filter[MAX_FLT];
int filter_len;
@@ -261,28 +260,28 @@ static int nvis(L *l, int out[MAX_APPS]) {
return n;
}
static void tab_layout(L *l, int widths[NCAT], int *total) {
static void pill_layout(L *l, int widths[NCAT], int *total) {
*total = 0;
for (int i = 0; i < NCAT; i++) {
XGlyphInfo e;
XftTextExtentsUtf8(l->dpy, l->font_ui, (FcChar8*)CATS[i], strlen(CATS[i]), &e);
widths[i] = (int)e.width + 22;
widths[i] = (int)e.width + 24;
*total += widths[i];
}
*total += (NCAT - 1) * PILL_GAP;
}
static int tab_at(L *l, int x, int y) {
if (y < HDR_H || y >= CONTENT_Y) return -1;
int w[NCAT], total; tab_layout(l, w, &total);
int w[NCAT], total; pill_layout(l, w, &total);
int tx = (WIN_W - total) / 2;
for (int i = 0; i < NCAT; i++) {
if (x >= tx && x < tx + w[i]) return i;
tx += w[i];
tx += w[i] + PILL_GAP;
}
return -1;
}
/* card index within visible slice (adjusted for scroll) */
static int card_at_vis(int x, int y) {
if (y < CONTENT_Y) return -1;
int lx = x - GX0, ly = y - GY0;
@@ -332,60 +331,68 @@ found:;
static void draw(L *l) {
XftDraw *xd = XftDrawCreate(l->dpy, l->win, l->visual, l->cmap);
/* background */
XSetForeground(l->dpy, l->gc, mkc(l, C_BG));
XFillRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W, WIN_H);
/* header */
XSetForeground(l->dpy, l->gc, mkc(l, C_HDR));
/* ── header: search bar ─────────────────────────── */
XSetForeground(l->dpy, l->gc, mkc(l, C_SURFACE));
XFillRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W, HDR_H);
XSetForeground(l->dpy, l->gc, mkc(l, C_ACCENT));
XFillRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W, 2);
ds(l, xd, l->font_logo, C_ACCENT, 14, 13, "OrionOS");
/* search box */
/* search input box */
{
int sx = 86, sy = 10, sw = WIN_W - 86 - 14, sh = 30;
XSetForeground(l->dpy, l->gc, mkc(l, C_SEARCH));
int sx = 16, sy = 11, sw = WIN_W - 32, sh = 36;
XSetForeground(l->dpy, l->gc, mkc(l, C_SRCH_BG));
XFillRectangle(l->dpy, l->win, l->gc, sx, sy, sw, sh);
XSetForeground(l->dpy, l->gc, mkc(l, C_SEARCH_BD));
XDrawRectangle(l->dpy, l->win, l->gc, sx, sy, sw, sh);
XSetForeground(l->dpy, l->gc, mkc(l,
l->filter_len > 0 ? C_BORDER_HV : C_SRCH_BD));
XDrawRectangle(l->dpy, l->win, l->gc, sx, sy, sw - 1, sh - 1);
/* accent left bar when active */
if (l->filter_len > 0) {
XSetForeground(l->dpy, l->gc, mkc(l, C_ACCENT));
XFillRectangle(l->dpy, l->win, l->gc, sx, sy, 2, sh);
}
if (l->filter_len == 0) {
ds(l, xd, l->font_ui, C_TEXT_DIM, sx + 8, sy + 7, "Search apps...");
ds(l, xd, l->font_ui, C_TEXT_DIM, sx + 12, sy + 10, "Search applications...");
} else {
char buf[MAX_FLT + 2];
snprintf(buf, sizeof(buf), "%s|", l->filter);
ds(l, xd, l->font_ui, C_TEXT_HOV, sx + 8, sy + 7, buf);
snprintf(buf, sizeof(buf), "%s_", l->filter);
ds(l, xd, l->font_ui, C_TEXT_HOV, sx + 12, sy + 10, buf);
}
}
/* header bottom separator */
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawLine(l->dpy, l->win, l->gc, 0, HDR_H, WIN_W, HDR_H);
XDrawLine(l->dpy, l->win, l->gc, 0, HDR_H - 1, WIN_W, HDR_H - 1);
/* ── pills row ───────────────────────────────────── */
int pw[NCAT], ptotal; pill_layout(l, pw, &ptotal);
int px = (WIN_W - ptotal) / 2;
int py = HDR_H + (PILLS_H - 24) / 2;
/* category tabs */
int tw[NCAT], ttotal; tab_layout(l, tw, &ttotal);
int tx = (WIN_W - ttotal) / 2;
for (int i = 0; i < NCAT; i++) {
if (i == l->sel_cat) {
XSetForeground(l->dpy, l->gc, mkc(l, C_TAB_SEL));
XFillRectangle(l->dpy, l->win, l->gc, tx, HDR_H + 1, tw[i], TAB_H - 1);
XSetForeground(l->dpy, l->gc, mkc(l, C_ACCENT));
XFillRectangle(l->dpy, l->win, l->gc, tx + 5, HDR_H + TAB_H - 2, tw[i] - 10, 2);
XSetForeground(l->dpy, l->gc, mkc(l, C_PILL_SEL));
XFillRectangle(l->dpy, l->win, l->gc, px, py, pw[i], 24);
XSetForeground(l->dpy, l->gc, mkc(l, C_PILL_BD));
XDrawRectangle(l->dpy, l->win, l->gc, px, py, pw[i] - 1, 23);
ds_ctr(l, xd, l->font_ui, C_TEXT_CAT_S, px, py, pw[i], 24, CATS[i]);
} else {
ds_ctr(l, xd, l->font_ui, C_TEXT_CAT, px, py, pw[i], 24, CATS[i]);
}
ds_ctr(l, xd, l->font_ui,
(i == l->sel_cat) ? C_TAB_TSEL : C_TAB_TXT,
tx, HDR_H, tw[i], TAB_H, CATS[i]);
tx += tw[i];
px += pw[i] + PILL_GAP;
}
/* pills bottom separator */
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawLine(l->dpy, l->win, l->gc, 0, CONTENT_Y, WIN_W, CONTENT_Y);
XDrawLine(l->dpy, l->win, l->gc, 0, CONTENT_Y - 1, WIN_W, CONTENT_Y - 1);
/* app grid with scroll */
/* ── app grid ────────────────────────────────────── */
int vis[MAX_APPS];
int nv = nvis(l, vis);
/* clamp scroll */
int max_scroll = (nv - ROWS_VIS * COLS + COLS - 1) / COLS;
if (max_scroll < 0) max_scroll = 0;
if (l->scroll > max_scroll) l->scroll = max_scroll;
@@ -395,7 +402,8 @@ static void draw(L *l) {
if (nv == 0) {
ds_ctr(l, xd, l->font_ui, C_TEXT_DIM,
0, CONTENT_Y, WIN_W, WIN_H - CONTENT_Y, "No apps found");
0, CONTENT_Y, WIN_W, WIN_H - CONTENT_Y - FOOTER_H,
"No matching apps");
} else {
for (int i = 0; i < ROWS_VIS * COLS; i++) {
int vi = vis_start + i;
@@ -406,18 +414,29 @@ static void draw(L *l) {
int cy = GY0 + row * (CARD_H + CARD_GAP);
int hover = (i == l->hov);
/* card background */
if (hover) {
XSetForeground(l->dpy, l->gc, mkc(l, C_CARD_HOV));
XSetForeground(l->dpy, l->gc, mkc(l, C_SURFACE_HV));
XFillRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W, CARD_H);
XSetForeground(l->dpy, l->gc, mkc(l, C_CARD_BD));
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER_HV));
XDrawRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W - 1, CARD_H - 1);
/* accent top bar on hover */
XSetForeground(l->dpy, l->gc, mkc(l, C_ACCENT));
XFillRectangle(l->dpy, l->win, l->gc, cx + 1, cy, CARD_W - 2, 2);
} else {
XSetForeground(l->dpy, l->gc, mkc(l, C_SURFACE));
XFillRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W, CARD_H);
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W - 1, CARD_H - 1);
}
int app_i = vis[vi];
unsigned int h = 0;
for (const char *p = APPS[app_i].name; *p; p++) h = h * 31 + (unsigned char)*p;
/* icon */
int ix = cx + (CARD_W - ICON_SZ) / 2;
int iy = cy + 10;
int iy = cy + 14;
int icon_rendered = 0;
#ifdef HAVE_IMLIB2
icon_rendered = render_icon(l, APPS[app_i].icon, ix, iy, ICON_SZ);
@@ -425,34 +444,57 @@ static void draw(L *l) {
if (!icon_rendered) {
XSetForeground(l->dpy, l->gc, mkc(l, ICON_BG[h % NICON]));
XFillRectangle(l->dpy, l->win, l->gc, ix, iy, ICON_SZ, ICON_SZ);
/* 1px darker border on icon box */
XSetForeground(l->dpy, l->gc, mkc(l, 0x000000));
XDrawRectangle(l->dpy, l->win, l->gc, ix, iy, ICON_SZ - 1, ICON_SZ - 1);
char letter[2] = { APPS[app_i].name[0], '\0' };
if (letter[0] >= 'a' && letter[0] <= 'z') letter[0] -= 32;
ds_ctr(l, xd, l->font_icon, 0xEAF0FF, ix, iy, ICON_SZ, ICON_SZ, letter);
ds_ctr(l, xd, l->font_icon, 0xF0F4FF,
ix, iy, ICON_SZ, ICON_SZ, letter);
}
char lbl[14];
snprintf(lbl, sizeof(lbl), "%.12s", APPS[app_i].name);
/* label */
char lbl[16];
snprintf(lbl, sizeof(lbl), "%.14s", APPS[app_i].name);
ds_ctr(l, xd, l->font_ui,
hover ? C_TEXT_HOV : C_TEXT,
cx, iy + ICON_SZ + 4, CARD_W, CARD_H - ICON_SZ - 18, lbl);
cx, iy + ICON_SZ + 6, CARD_W, CARD_H - ICON_SZ - 24, lbl);
}
}
/* scrollbar */
if (max_scroll > 0) {
int sb_x = WIN_W - 8, sb_y = CONTENT_Y + 4;
int sb_h = WIN_H - CONTENT_Y - 8;
int sb_x = WIN_W - 6;
int sb_y = CONTENT_Y + 4;
int sb_h = WIN_H - CONTENT_Y - FOOTER_H - 8;
XSetForeground(l->dpy, l->gc, mkc(l, C_SCROLL_BG));
XFillRectangle(l->dpy, l->win, l->gc, sb_x, sb_y, 6, sb_h);
XFillRectangle(l->dpy, l->win, l->gc, sb_x, sb_y, 4, sb_h);
int total_rows = (nv + COLS - 1) / COLS;
int thumb_h = sb_h * ROWS_VIS / total_rows;
if (thumb_h < 12) thumb_h = 12;
if (thumb_h < 10) thumb_h = 10;
int thumb_y = sb_y + (sb_h - thumb_h) * l->scroll / max_scroll;
XSetForeground(l->dpy, l->gc, mkc(l, C_SCROLL_TH));
XFillRectangle(l->dpy, l->win, l->gc, sb_x + 1, thumb_y, 4, thumb_h);
XFillRectangle(l->dpy, l->win, l->gc, sb_x, thumb_y, 4, thumb_h);
}
/* outer border */
/* ── footer ──────────────────────────────────────── */
int fy = WIN_H - FOOTER_H;
XSetForeground(l->dpy, l->gc, mkc(l, C_FOOTER_BG));
XFillRectangle(l->dpy, l->win, l->gc, 0, fy, WIN_W, FOOTER_H);
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawLine(l->dpy, l->win, l->gc, 0, fy, WIN_W, fy);
/* app count left, hints right */
{
char cnt[32];
snprintf(cnt, sizeof(cnt), "%d apps", nv);
ds(l, xd, l->font_hint, C_FOOTER_TXT, 14, fy + 7, cnt);
ds_ctr(l, xd, l->font_hint, C_FOOTER_TXT,
0, fy, WIN_W, FOOTER_H,
"Return: launch Esc: close Type: filter");
}
/* outer window border */
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W - 1, WIN_H - 1);
@@ -482,7 +524,6 @@ static void on_key(L *l, XKeyEvent *e) {
l->filter[--l->filter_len] = '\0';
l->scroll = 0; l->hov = -1; draw(l); return;
}
/* page up/down for keyboard scroll */
if (sym == XK_Prior) { l->scroll -= ROWS_VIS; if (l->scroll < 0) l->scroll = 0; draw(l); return; }
if (sym == XK_Next) { l->scroll += ROWS_VIS; draw(l); return; }
@@ -510,11 +551,11 @@ int main(void) {
l.visual = DefaultVisual(l.dpy, l.screen);
l.font_ui = XftFontOpenName(l.dpy, l.screen, FONT_UI);
l.font_logo = XftFontOpenName(l.dpy, l.screen, FONT_LOGO);
l.font_icon = XftFontOpenName(l.dpy, l.screen, FONT_ICON);
l.font_hint = XftFontOpenName(l.dpy, l.screen, FONT_HINT);
if (!l.font_ui) l.font_ui = XftFontOpenName(l.dpy, l.screen, "fixed:size=9");
if (!l.font_logo) l.font_logo = l.font_ui;
if (!l.font_icon) l.font_icon = l.font_ui;
if (!l.font_hint) l.font_hint = l.font_ui;
int sw = DisplayWidth(l.dpy, l.screen);
int sh = DisplayHeight(l.dpy, l.screen);
@@ -544,14 +585,13 @@ int main(void) {
if (e.xexpose.count == 0) draw(&l);
break;
case ButtonPress:
if (e.xbutton.button == Button4) {
l.scroll -= 1; if (l.scroll < 0) l.scroll = 0; draw(&l); break;
}
if (e.xbutton.button == Button5) {
l.scroll += 1; draw(&l); break;
}
{
/* scroll wheel */
if (e.xbutton.button == Button4) {
l.scroll -= 1; if (l.scroll < 0) l.scroll = 0; draw(&l); break;
}
if (e.xbutton.button == Button5) {
l.scroll += 1; draw(&l); break;
}
int t = tab_at(&l, e.xbutton.x, e.xbutton.y);
if (t >= 0) {
l.sel_cat = t; l.scroll = 0; l.hov = -1; draw(&l);
+15 -16
View File
@@ -6,22 +6,21 @@
static const MenuItem MENU_ITEMS[] = {
{ "App Launcher", "orionlauncher &" },
{ "Terminal", "alacritty &" },
{ "File Manager", "rigel &" },
{ "Text Editor", "quill &" },
{ "──────────────────", NULL },
{ "Web Browser", "firefox &" },
{ "System Monitor", "alacritty -T 'System Monitor' -e htop &" },
{ "Volume Control", "pavucontrol &" },
{ "System Settings", "nebula &" },
{ "Software Updates", "orionpkg &" },
{ "Display Settings", "arandr &" },
{ "──────────────────", NULL },
{ "Change Wallpaper", "orionwallpaper --reset &" },
{ "Install OrionOS", "alacritty -e bash -c 'sudo orioninstall-gui 2>/dev/null || sudo orioninstall; exec bash' &" },
{ "──────────────────", NULL },
{ "Restart OrionWM", "pkill orionwm; sleep 0.2; orionwm &" },
{ "Quit OrionOS", "pkill orionwm" },
{ "Terminal", "xterm &" },
{ "File Manager", "pcmanfm &" },
{ "Text Editor", "mousepad &" },
{ "──────────────────", NULL },
{ "Web Browser", "firefox &" },
{ "System Monitor", "xterm -T 'System Monitor' -e htop &" },
{ "Volume Control", "xterm -e alsamixer &" },
{ "Software Updates", "orionpkg &" },
{ "Display Settings", "arandr &" },
{ "──────────────────", NULL },
{ "Change Wallpaper", "orionwallpaper --reset &" },
{ "Install OrionOS", "pkexec orioninstall-gui &" },
{ "──────────────────", NULL },
{ "Restart OrionWM", "pkill orionwm; sleep 0.2; orionwm &" },
{ "Quit OrionOS", "pkill orionwm" },
};
#define MENU_COUNT ((int)(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0])))