Initial commit: SkyOS source code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 4.6 committed 2026-07-23 12:14:34 -04:00
commit e641720a3c
30 files changed
+5836

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
CC = gcc
PKG_XFT_C := $(shell pkg-config --cflags xft x11 2>/dev/null)
PKG_XFT_L := $(shell pkg-config --libs xft x11 2>/dev/null)
ifeq ($(PKG_XFT_C),)
PKG_XFT_C := -I/opt/homebrew/include
PKG_XFT_L := -L/opt/homebrew/lib -lXft -lfontconfig
endif
CFLAGS = -Wall -Wextra -O2 $(PKG_XFT_C)
LDFLAGS = $(PKG_XFT_L)
SRC = src/launcher.c
BIN = skylauncher
.PHONY: all clean install
all: $(BIN)
$(BIN): $(SRC)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
install: $(BIN)
install -Dm755 $(BIN) /usr/local/bin/$(BIN)
clean:
rm -f $(BIN)
+407
View File
@@ -0,0 +1,407 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/Xft/Xft.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* ── dimensions ─────────────────────────────────────────────────────── */
#define WIN_W 580
#define WIN_H 420
#define HDR_H 50 /* search header */
#define TAB_H 34 /* category tab strip */
#define CONTENT_Y (HDR_H + TAB_H)
#define PAD 12 /* grid outer margin */
#define CARD_W 96
#define CARD_H 84
#define CARD_GAP 8
#define COLS 5
#define ICON_SZ 38
/* grid left edge */
#define GX0 ((WIN_W - (COLS * CARD_W + (COLS-1) * CARD_GAP)) / 2)
#define GY0 (CONTENT_Y + PAD)
/* ── 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
static const unsigned long ICON_BG[] = {
0x1A4D88, 0x166C3C, 0x6C3018,
0x4A1A80, 0x126070, 0x6C1A38,
0x365218, 0x183460,
};
#define NICON 8
#define FONT_UI "Sans:size=9"
#define FONT_LOGO "Sans:size=11:weight=bold"
#define FONT_ICON "Sans:size=13:weight=bold"
#define MAX_FLT 64
typedef struct { const char *name, *cmd, *cat; } App;
static const App APPS[] = {
{ "Firefox", "firefox &", "Internet" },
{ "Email", "firefox https://mail.google.com &", "Internet" },
{ "Media Player", "mpv --player-operation-mode=pseudo-gui &", "Media" },
{ "Images", "viewnior &", "Media" },
{ "OnlyOffice", "onlyoffice-desktopeditors &", "Office" },
{ "PDF Viewer", "evince &", "Office" },
{ "Terminal", "xterm &", "System" },
{ "Files", "thunar &", "System" },
{ "Text Editor", "mousepad &", "System" },
{ "System Info", "xterm -e 'fastfetch; read' &", "System" },
{ "Screen Lock", "xscreensaver-command -lock &", "System" },
{ "Install", "xterm -e 'sudo skyinstall' &", "System" },
{ "Display", "arandr &", "Settings" },
{ "Audio", "pavucontrol &", "Settings" },
{ "Network", "nm-connection-editor &", "Settings" },
{ "Bluetooth", "blueman-manager &", "Settings" },
};
#define NAPP ((int)(sizeof(APPS)/sizeof(APPS[0])))
static const char *CATS[] = {
"All", "Internet", "Media", "Office", "System", "Settings"
};
#define NCAT ((int)(sizeof(CATS)/sizeof(CATS[0])))
typedef struct {
Display *dpy; int screen;
Window root, win; GC gc;
Colormap cmap; Visual *visual;
XftFont *font_ui, *font_logo, *font_icon;
int sel_cat, hov;
char filter[MAX_FLT];
int filter_len;
} L;
/* ── helpers ─────────────────────────────────────────────────────────── */
static unsigned long mkc(L *l, 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(l->dpy, l->cmap, &c);
return c.pixel;
}
static void xftc(L *l, unsigned long rgb, XftColor *out) {
XRenderColor r = {
((rgb>>16)&0xFF)*257, ((rgb>>8)&0xFF)*257,
((rgb) &0xFF)*257, 0xFFFF
};
XftColorAllocValue(l->dpy, l->visual, l->cmap, &r, out);
}
static void ds(L *l, XftDraw *xd, XftFont *f, unsigned long rgb,
int x, int y, const char *s) {
XftColor c; xftc(l, rgb, &c);
XGlyphInfo e;
XftTextExtentsUtf8(l->dpy, f, (FcChar8*)s, strlen(s), &e);
XftDrawStringUtf8(xd, &c, f, x, y + e.height - e.y, (FcChar8*)s, strlen(s));
XftColorFree(l->dpy, l->visual, l->cmap, &c);
}
static void ds_ctr(L *l, XftDraw *xd, XftFont *f, unsigned long rgb,
int bx, int by, int bw, int bh, const char *s) {
XGlyphInfo e;
XftTextExtentsUtf8(l->dpy, f, (FcChar8*)s, strlen(s), &e);
int tx = bx + (bw - (int)e.width) / 2;
int ty = by + (bh - (int)e.height) / 2;
ds(l, xd, f, rgb, tx, ty, s);
}
/* ── query ───────────────────────────────────────────────────────────── */
static int nvis(L *l, int out[NAPP]) {
int n = 0;
const char *cat = CATS[l->sel_cat];
for (int i = 0; i < NAPP; i++) {
if (strcmp(cat,"All")!=0 && strcmp(APPS[i].cat,cat)!=0) continue;
if (l->filter_len > 0) {
char nm[64], fl[MAX_FLT];
snprintf(nm, sizeof(nm), "%s", APPS[i].name);
snprintf(fl, sizeof(fl), "%s", l->filter);
for (char *p=nm; *p; p++) if (*p>='A'&&*p<='Z') *p+=32;
for (char *p=fl; *p; p++) if (*p>='A'&&*p<='Z') *p+=32;
if (!strstr(nm, fl)) continue;
}
out[n++] = i;
}
return n;
}
/* tab widths — measured once per draw via font metrics */
static void tab_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;
*total += widths[i];
}
}
/* hit: which category tab contains point (x,y)? -1 = none */
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 tx = (WIN_W - total) / 2;
for (int i = 0; i < NCAT; i++) {
if (x >= tx && x < tx + w[i]) return i;
tx += w[i];
}
return -1;
}
/* hit: which card index (into vis[]) is at point? -1 = none */
static int card_at(int x, int y) {
if (y < CONTENT_Y) return -1;
int lx = x - GX0;
int ly = y - GY0;
if (lx < 0 || ly < 0) return -1;
int col = lx / (CARD_W + CARD_GAP);
int row = ly / (CARD_H + CARD_GAP);
if (col < 0 || col >= COLS || row < 0) return -1;
/* reject click in gap between cards */
int cx = col * (CARD_W + CARD_GAP);
int cy = row * (CARD_H + CARD_GAP);
if (lx - cx >= CARD_W || ly - cy >= CARD_H) return -1;
return row * COLS + col;
}
/* ── draw ─────────────────────────────────────────────────────────────── */
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));
XFillRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W, HDR_H);
/* accent top stripe */
XSetForeground(l->dpy, l->gc, mkc(l, C_ACCENT));
XFillRectangle(l->dpy, l->win, l->gc, 0, 0, WIN_W, 2);
/* logo */
ds(l, xd, l->font_logo, C_ACCENT, 14, 13, "SkyOS");
/* search box */
{
int sx = 86, sy = 10, sw = WIN_W - 86 - 14, sh = 30;
XSetForeground(l->dpy, l->gc, mkc(l, C_SEARCH));
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);
if (l->filter_len == 0) {
ds(l, xd, l->font_ui, C_TEXT_DIM, sx+8, sy+7, "Search...");
} 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);
}
}
/* separator below header */
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawLine(l->dpy, l->win, l->gc, 0, HDR_H, WIN_W, HDR_H);
/* 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);
/* accent underline */
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);
}
unsigned long tc = (i == l->sel_cat) ? C_TAB_TSEL : C_TAB_TXT;
ds_ctr(l, xd, l->font_ui, tc, tx, HDR_H, tw[i], TAB_H, CATS[i]);
tx += tw[i];
}
/* separator below tabs */
XSetForeground(l->dpy, l->gc, mkc(l, C_BORDER));
XDrawLine(l->dpy, l->win, l->gc, 0, CONTENT_Y, WIN_W, CONTENT_Y);
/* app grid */
int vis[NAPP];
int nv = nvis(l, vis);
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");
} else {
for (int i = 0; i < nv; i++) {
int col = i % COLS;
int row = i / COLS;
int cx = GX0 + col * (CARD_W + CARD_GAP);
int cy = GY0 + row * (CARD_H + CARD_GAP);
if (cy + CARD_H > WIN_H) break; /* clip to window */
int hover = (i == l->hov);
/* card hover highlight */
if (hover) {
XSetForeground(l->dpy, l->gc, mkc(l, C_CARD_HOV));
XFillRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W, CARD_H);
XSetForeground(l->dpy, l->gc, mkc(l, C_CARD_BD));
XDrawRectangle(l->dpy, l->win, l->gc, cx, cy, CARD_W-1, CARD_H-1);
}
/* icon square */
int app_i = vis[i];
unsigned int h = 0;
for (const char *p = APPS[app_i].name; *p; p++) h = h*31 + (unsigned char)*p;
unsigned long ibg = ICON_BG[h % NICON];
int ix = cx + (CARD_W - ICON_SZ) / 2;
int iy = cy + 10;
XSetForeground(l->dpy, l->gc, mkc(l, ibg));
XFillRectangle(l->dpy, l->win, l->gc, ix, iy, ICON_SZ, ICON_SZ);
/* icon letter */
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);
/* label */
char lbl[14];
snprintf(lbl, sizeof(lbl), "%.12s", APPS[app_i].name);
unsigned long tc = hover ? C_TEXT_HOV : C_TEXT;
ds_ctr(l, xd, l->font_ui, tc,
cx, iy + ICON_SZ + 4, CARD_W, CARD_H - ICON_SZ - 18, lbl);
}
}
/* outer 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);
XftDrawDestroy(xd);
}
/* ── event handlers ──────────────────────────────────────────────────── */
static void launch(L *l, int hov) {
int vis[NAPP];
int nv = nvis(l, vis);
if (hov >= 0 && hov < nv) {
system(APPS[vis[hov]].cmd);
XDestroyWindow(l->dpy, l->win);
XCloseDisplay(l->dpy);
exit(0);
}
}
static void on_key(L *l, XKeyEvent *e) {
KeySym sym = XLookupKeysym(e, 0);
if (sym == XK_Escape) {
XDestroyWindow(l->dpy, l->win);
XCloseDisplay(l->dpy);
exit(0);
}
if (sym == XK_Return) { launch(l, l->hov); return; }
if (sym == XK_BackSpace && l->filter_len > 0) {
l->filter[--l->filter_len] = '\0';
l->hov = -1; draw(l); return;
}
char buf[8] = {0};
int len = XLookupString(e, buf, sizeof(buf)-1, &sym, NULL);
if (len > 0 && buf[0] >= 0x20 && buf[0] < 0x7F && l->filter_len < MAX_FLT-1) {
l->filter[l->filter_len++] = buf[0];
l->filter[l->filter_len] = '\0';
l->sel_cat = 0; l->hov = -1;
draw(l);
}
}
/* ── main ─────────────────────────────────────────────────────────────── */
int main(void) {
L l = {0}; l.sel_cat = 0; l.hov = -1;
l.dpy = XOpenDisplay(NULL);
if (!l.dpy) { fprintf(stderr, "skylauncher: cannot open display\n"); return 1; }
l.screen = DefaultScreen(l.dpy);
l.root = RootWindow(l.dpy, l.screen);
l.cmap = DefaultColormap(l.dpy, l.screen);
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);
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;
int sw = DisplayWidth(l.dpy, l.screen);
int sh = DisplayHeight(l.dpy, l.screen);
XSetWindowAttributes swa;
swa.background_pixel = mkc(&l, C_BG);
swa.override_redirect = True;
swa.event_mask = ExposureMask | ButtonPressMask
| PointerMotionMask | LeaveWindowMask | KeyPressMask;
l.win = XCreateWindow(
l.dpy, l.root,
(sw - WIN_W) / 2, (sh - WIN_H) / 2, WIN_W, WIN_H, 0,
DefaultDepth(l.dpy, l.screen), InputOutput, l.visual,
CWBackPixel | CWOverrideRedirect | CWEventMask, &swa);
l.gc = XCreateGC(l.dpy, l.win, 0, NULL);
XMapRaised(l.dpy, l.win);
XGrabKeyboard(l.dpy, l.win, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XSync(l.dpy, False);
for (;;) {
XEvent e;
XNextEvent(l.dpy, &e);
switch (e.type) {
case Expose:
if (e.xexpose.count == 0) draw(&l);
break;
case ButtonPress:
{
int t = tab_at(&l, e.xbutton.x, e.xbutton.y);
if (t >= 0) {
l.sel_cat = t; l.hov = -1; draw(&l);
} else {
int idx = card_at(e.xbutton.x, e.xbutton.y);
launch(&l, idx);
}
}
break;
case MotionNotify:
{
int idx = card_at(e.xmotion.x, e.xmotion.y);
int vis[NAPP]; int nv = nvis(&l, vis);
int prev = l.hov;
l.hov = (idx >= 0 && idx < nv) ? idx : -1;
if (l.hov != prev) draw(&l);
}
break;
case LeaveNotify:
if (l.hov != -1) { l.hov = -1; draw(&l); }
break;
case KeyPress:
on_key(&l, &e.xkey);
break;
}
}
return 0;
}