Phase 1/2 complete: system tray (XEMBED) + Plymouth boot splash
System tray in skydeskbar (Phase 1 #4): - Claims _NET_SYSTEM_TRAY_S<screen> selection on startup and broadcasts MANAGER so nm-applet and other tray clients connect - Handles _NET_SYSTEM_TRAY_OPCODE dock requests: XReparentWindow icon into the deskbar, sends XEMBED_EMBEDDED_NOTIFY - Tray section is zero-height when empty; expands to DB_TRAY_H=26px when first icon embeds, shrinking back when last icon exits - DestroyNotify / ReparentNotify clean up the icon list and reposition remaining icons; SelectionClear hands off gracefully - Icon positions are updated (db_tray_reposition) whenever the app list changes and shifts the tray section vertically Plymouth boot splash (Phase 2 #10): - 'skyos' script-module theme: dark navy (#111520) background, "SkyOS" logo in accent blue (#90BBEE), thin progress bar - 1×1 pixel bar_bg.png / bar_fg.png scaled at runtime by the Plymouth script — no large PNGs committed to the repo - logo.png generated at build time by ImageMagick (same tool used for the wallpaper); build continues if ImageMagick is absent - mkinitcpio HOOKS: added 'plymouth' after 'udev' - Kernel cmdline: added 'splash' for UEFI systemd-boot entry - customize_airootfs.sh: plymouth-set-default-theme skyos Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
2b8ea90a57
commit
803ffe991f
3 files changed
+227
-12
No files matched your search
+130
-9
@@ -1,5 +1,9 @@
|
||||
#include "deskbar.h"
|
||||
|
||||
#define TRAY_REQUEST_DOCK 0
|
||||
#define XEMBED_EMBEDDED_NOTIFY 0
|
||||
#define XEMBED_VERSION 0
|
||||
|
||||
/* ── layout ───────────────────────────────────────────────────────────── */
|
||||
|
||||
int db_apps_y(void) { return DB_TOP_H + 1; }
|
||||
@@ -8,8 +12,11 @@ int db_ws_y(Deskbar *db) {
|
||||
return db_apps_y() + db->napps * DB_ITEM_H + 1;
|
||||
}
|
||||
|
||||
static int db_tray_h(Deskbar *db) { return db->n_tray_icons > 0 ? DB_TRAY_H : 0; }
|
||||
static int db_tray_y(Deskbar *db) { return db_ws_y(db) + DB_WS_H + 1; }
|
||||
static int db_status_y(Deskbar *db) {
|
||||
return db_ws_y(db) + DB_WS_H + 1;
|
||||
int th = db_tray_h(db);
|
||||
return db_tray_y(db) + th + (th > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
void db_resize(Deskbar *db) {
|
||||
@@ -141,6 +148,65 @@ void db_update_workspace(Deskbar *db) {
|
||||
db->num_ws = (nws > 0) ? (int)nws : 4;
|
||||
}
|
||||
|
||||
/* ── system tray (XEmbed) ─────────────────────────────────────────────── */
|
||||
|
||||
static void db_tray_reposition(Deskbar *db) {
|
||||
int ty = db_tray_y(db);
|
||||
int iy = (DB_TRAY_H - DB_TRAY_ICON_SZ) / 2;
|
||||
for (int i = 0; i < db->n_tray_icons; i++) {
|
||||
int ix = 4 + i * (DB_TRAY_ICON_SZ + 4);
|
||||
XMoveWindow(db->dpy, db->tray_icons[i], ix, ty + iy);
|
||||
}
|
||||
}
|
||||
|
||||
static void db_tray_dock(Deskbar *db, Window icon_win) {
|
||||
if (db->n_tray_icons >= DB_TRAY_MAX) return;
|
||||
for (int i = 0; i < db->n_tray_icons; i++)
|
||||
if (db->tray_icons[i] == icon_win) return;
|
||||
|
||||
int ty = db_tray_y(db);
|
||||
int iy = (DB_TRAY_H - DB_TRAY_ICON_SZ) / 2;
|
||||
int ix = 4 + db->n_tray_icons * (DB_TRAY_ICON_SZ + 4);
|
||||
int was_empty = (db->n_tray_icons == 0);
|
||||
|
||||
XSelectInput(db->dpy, icon_win, StructureNotifyMask);
|
||||
XReparentWindow(db->dpy, icon_win, db->win, ix, ty + iy);
|
||||
XResizeWindow(db->dpy, icon_win, DB_TRAY_ICON_SZ, DB_TRAY_ICON_SZ);
|
||||
XMapWindow(db->dpy, icon_win);
|
||||
|
||||
/* XEMBED_EMBEDDED_NOTIFY message to the icon */
|
||||
XEvent ev = {0};
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = icon_win;
|
||||
ev.xclient.message_type = db->atom_xembed;
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY;
|
||||
ev.xclient.data.l[2] = 0;
|
||||
ev.xclient.data.l[3] = db->win;
|
||||
ev.xclient.data.l[4] = XEMBED_VERSION;
|
||||
XSendEvent(db->dpy, icon_win, False, NoEventMask, &ev);
|
||||
|
||||
db->tray_icons[db->n_tray_icons++] = icon_win;
|
||||
if (was_empty) db_resize(db);
|
||||
db_draw(db);
|
||||
}
|
||||
|
||||
static void db_tray_remove(Deskbar *db, Window icon_win) {
|
||||
int pos = -1;
|
||||
for (int i = 0; i < db->n_tray_icons; i++)
|
||||
if (db->tray_icons[i] == icon_win) { pos = i; break; }
|
||||
if (pos < 0) return;
|
||||
|
||||
memmove(&db->tray_icons[pos], &db->tray_icons[pos + 1],
|
||||
(db->n_tray_icons - pos - 1) * sizeof(Window));
|
||||
db->n_tray_icons--;
|
||||
|
||||
db_tray_reposition(db);
|
||||
if (db->n_tray_icons == 0) db_resize(db);
|
||||
db_draw(db);
|
||||
}
|
||||
|
||||
/* ── system status ────────────────────────────────────────────────────── */
|
||||
|
||||
static int read_cpu_percent(void) {
|
||||
@@ -429,6 +495,17 @@ void db_draw(Deskbar *db) {
|
||||
XDrawLine(db->dpy, db->win, db->gc,
|
||||
0, wy + DB_WS_H, DB_W, wy + DB_WS_H);
|
||||
|
||||
/* ── SYSTEM TRAY ── */
|
||||
if (db->n_tray_icons > 0) {
|
||||
int ty = db_tray_y(db);
|
||||
XSetForeground(db->dpy, db->gc, db_color(db, DB_COL_BG));
|
||||
XFillRectangle(db->dpy, db->win, db->gc, 0, ty, DB_W, DB_TRAY_H);
|
||||
|
||||
/* separator below tray */
|
||||
XSetForeground(db->dpy, db->gc, db_color(db, DB_COL_BORDER_D));
|
||||
XDrawLine(db->dpy, db->win, db->gc, 0, ty + DB_TRAY_H, DB_W, ty + DB_TRAY_H);
|
||||
}
|
||||
|
||||
/* ── STATUS BAR ── */
|
||||
int sy = db_status_y(db);
|
||||
XSetForeground(db->dpy, db->gc, db_color(db, DB_COL_BG));
|
||||
@@ -800,13 +877,14 @@ void db_init(Deskbar *db) {
|
||||
db->cmap = DefaultColormap(db->dpy, db->screen);
|
||||
db->visual = DefaultVisual(db->dpy, db->screen);
|
||||
db->depth = DefaultDepth(db->dpy, db->screen);
|
||||
db->hovered = -1;
|
||||
db->menu_win = None;
|
||||
db->sub_win = None;
|
||||
db->menu_hov = -1;
|
||||
db->sub_active = -1;
|
||||
db->sub_hov = -1;
|
||||
db->sw_cache = 0;
|
||||
db->hovered = -1;
|
||||
db->menu_win = None;
|
||||
db->sub_win = None;
|
||||
db->menu_hov = -1;
|
||||
db->sub_active = -1;
|
||||
db->sub_hov = -1;
|
||||
db->sw_cache = 0;
|
||||
db->n_tray_icons = 0;
|
||||
|
||||
db->net_client_list = XInternAtom(db->dpy, "_NET_CLIENT_LIST", False);
|
||||
db->net_active_window = XInternAtom(db->dpy, "_NET_ACTIVE_WINDOW", False);
|
||||
@@ -815,6 +893,15 @@ void db_init(Deskbar *db) {
|
||||
db->net_wm_name = XInternAtom(db->dpy, "_NET_WM_NAME", False);
|
||||
db->utf8_string = XInternAtom(db->dpy, "UTF8_STRING", False);
|
||||
|
||||
{
|
||||
char sel_name[32];
|
||||
snprintf(sel_name, sizeof(sel_name), "_NET_SYSTEM_TRAY_S%d", db->screen);
|
||||
db->atom_tray_s0 = XInternAtom(db->dpy, sel_name, False);
|
||||
}
|
||||
db->atom_tray_opcode = XInternAtom(db->dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
|
||||
db->atom_xembed = XInternAtom(db->dpy, "_XEMBED", False);
|
||||
db->atom_manager = XInternAtom(db->dpy, "MANAGER", False);
|
||||
|
||||
db->font_ui = XftFontOpenName(db->dpy, db->screen, DB_FONT_UI);
|
||||
db->font_menu = XftFontOpenName(db->dpy, db->screen, DB_FONT_MENU);
|
||||
db->font_clock = XftFontOpenName(db->dpy, db->screen, DB_FONT_CLOCK);
|
||||
@@ -829,7 +916,7 @@ void db_init(Deskbar *db) {
|
||||
db_update_apps(db);
|
||||
db_update_workspace(db);
|
||||
|
||||
db->bar_h = DB_TOP_H + 1 + db->napps * DB_ITEM_H + 1 + DB_WS_H + 1 + DB_STATUS_H + 2;
|
||||
db->bar_h = db_status_y(db) + DB_STATUS_H + 2;
|
||||
|
||||
int sw = DisplayWidth(db->dpy, db->screen);
|
||||
|
||||
@@ -847,6 +934,20 @@ void db_init(Deskbar *db) {
|
||||
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
|
||||
db->gc = XCreateGC(db->dpy, db->win, 0, NULL);
|
||||
|
||||
/* Claim _NET_SYSTEM_TRAY_S<screen> selection and announce ourselves */
|
||||
XSetSelectionOwner(db->dpy, db->atom_tray_s0, db->win, CurrentTime);
|
||||
if (XGetSelectionOwner(db->dpy, db->atom_tray_s0) == db->win) {
|
||||
XEvent sev = {0};
|
||||
sev.xclient.type = ClientMessage;
|
||||
sev.xclient.window = db->root;
|
||||
sev.xclient.message_type = db->atom_manager;
|
||||
sev.xclient.format = 32;
|
||||
sev.xclient.data.l[0] = CurrentTime;
|
||||
sev.xclient.data.l[1] = db->atom_tray_s0;
|
||||
sev.xclient.data.l[2] = db->win;
|
||||
XSendEvent(db->dpy, db->root, False, StructureNotifyMask, &sev);
|
||||
}
|
||||
|
||||
Atom net_wm_window_type = XInternAtom(db->dpy, "_NET_WM_WINDOW_TYPE", False);
|
||||
Atom net_wm_type_dock = XInternAtom(db->dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
|
||||
XChangeProperty(db->dpy, db->win, net_wm_window_type, XA_ATOM, 32,
|
||||
@@ -943,10 +1044,30 @@ void db_run(Deskbar *db) {
|
||||
e.xvisibility.state != VisibilityUnobscured)
|
||||
XRaiseWindow(db->dpy, db->win);
|
||||
break;
|
||||
case ClientMessage:
|
||||
if (e.xclient.message_type == db->atom_tray_opcode &&
|
||||
e.xclient.data.l[1] == TRAY_REQUEST_DOCK)
|
||||
db_tray_dock(db, (Window)e.xclient.data.l[2]);
|
||||
break;
|
||||
case DestroyNotify:
|
||||
db_tray_remove(db, e.xdestroywindow.window);
|
||||
break;
|
||||
case ReparentNotify:
|
||||
if (e.xreparent.parent != db->win)
|
||||
db_tray_remove(db, e.xreparent.window);
|
||||
break;
|
||||
case SelectionClear:
|
||||
if (e.xselectionclear.selection == db->atom_tray_s0) {
|
||||
db->n_tray_icons = 0;
|
||||
db_resize(db);
|
||||
db_draw(db);
|
||||
}
|
||||
break;
|
||||
case PropertyNotify:
|
||||
if (e.xproperty.atom == db->net_client_list) {
|
||||
/* Window list changed — full refresh + resize */
|
||||
db_update_apps(db);
|
||||
db_tray_reposition(db);
|
||||
db_resize(db);
|
||||
db_draw(db);
|
||||
} else if (e.xproperty.atom == db->net_active_window) {
|
||||
|
||||
Reference in new issue
Block a user