Add hot corners, media keys, orionshot, orionclip, notification history, autostart, GTK accent update, wallpaper slideshow, ISO build

- wm: hot corner polling via select()-based wm_run loop; TL=Exposé, TR=show desktop, BL=launcher, BR=lock
- wm: XF86 media key grabs (volume, brightness, playback) + Super+V for clipboard
- wm: autostart reads ~/.config/orionos/autostart on startup
- events: media key dispatch (amixer/brightnessctl/playerctl); Super+V → orionclip --show; Print → orionshot
- orionshot: new X11 screenshot tool, full-screen and rubber-band region modes, saves PNG to ~/Pictures/Screenshots/
- orionclip: new clipboard manager daemon; polls clipboard, stores 20 entries, popup via Super+V IPC
- orionnotify: append-only notification history log at ~/.local/share/orionos/notif-history.log
- themes: GTK accent updated from #3A6898 to #1E58A8 to match WM/deskbar
- themes: orionwallpaper gains --slideshow DIR [SECS] mode using feh + sleep loop
- iso: Docker-based archiso builder, live-session xinitrc, profiledef.sh
- Makefile: add orionshot and orionclip to all/clean/install; Docker-based iso target
This commit is contained in:
Claude committed 2026-08-30 20:47:47 +00:00
1 parent 2a42b826fb
commit a2092fd989
15 files changed
+957 -84

No files matched your search

+4
View File
@@ -5,12 +5,15 @@
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/Xft/Xft.h>
#include <X11/cursorfont.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
/* ── palette (macOS dark mode) ───────────────────────────────────────── */
#define COLOR_TITLE_ACTIVE 0x3C3C3C /* macOS dark titlebar */
@@ -113,6 +116,7 @@ typedef struct WM {
Client *focused;
int cur_desktop; /* active virtual desktop (0-based) */
int hot_corner_last; /* last hot-corner zone (0=none,1-4) */
Atom wm_delete_window;
Atom wm_protocols;
+36 -5
View File
@@ -392,16 +392,47 @@ void on_key_press(WM *wm, XKeyEvent *e) {
if ((e->state & Mod1Mask) && sym == XK_F1)
system("orionlauncher &");
/* PrintScreen — full screenshot to ~/Pictures */
/* PrintScreen — screenshot */
if (sym == XK_Print) {
if (e->state & ShiftMask)
system("mkdir -p ~/Pictures && scrot -s ~/Pictures/screenshot_%Y%m%d_%H%M%S.png"
" && notify-send 'Screenshot' 'Region saved to ~/Pictures' &");
system("orionshot region &");
else
system("mkdir -p ~/Pictures && scrot ~/Pictures/screenshot_%Y%m%d_%H%M%S.png"
" && notify-send 'Screenshot' 'Saved to ~/Pictures' &");
system("orionshot full &");
}
/* XF86 media keys */
if (sym == XF86XK_AudioRaiseVolume)
system("pactl set-sink-volume @DEFAULT_SINK@ +5% 2>/dev/null; "
"pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null | "
"grep -oP '[0-9]+%' | head -1 | "
"xargs -I{} notify-send -t 1500 'Volume' '{}' 2>/dev/null &");
if (sym == XF86XK_AudioLowerVolume)
system("pactl set-sink-volume @DEFAULT_SINK@ -5% 2>/dev/null; "
"pactl get-sink-volume @DEFAULT_SINK@ 2>/dev/null | "
"grep -oP '[0-9]+%' | head -1 | "
"xargs -I{} notify-send -t 1500 'Volume' '{}' 2>/dev/null &");
if (sym == XF86XK_AudioMute)
system("pactl set-sink-mute @DEFAULT_SINK@ toggle 2>/dev/null; "
"notify-send -t 1500 'Volume' 'Muted toggled' 2>/dev/null &");
if (sym == XF86XK_AudioPlay)
system("playerctl play-pause 2>/dev/null &");
if (sym == XF86XK_AudioNext)
system("playerctl next 2>/dev/null &");
if (sym == XF86XK_AudioPrev)
system("playerctl previous 2>/dev/null &");
if (sym == XF86XK_MonBrightnessUp)
system("brightnessctl set +10% 2>/dev/null; "
"notify-send -t 1200 'Brightness' "
"\"$(brightnessctl g 2>/dev/null)\" 2>/dev/null &");
if (sym == XF86XK_MonBrightnessDown)
system("brightnessctl set 10%- 2>/dev/null; "
"notify-send -t 1200 'Brightness' "
"\"$(brightnessctl g 2>/dev/null)\" 2>/dev/null &");
/* Super+V — clipboard manager */
if ((e->state & Mod4Mask) && sym == XK_v)
system("orionclip --show &");
/* Super+Left — snap left */
if ((e->state & Mod4Mask) && sym == XK_Left && wm->focused)
client_snap_left(wm, wm->focused);
+105 -16
View File
@@ -1,5 +1,67 @@
#include "wm.h"
/* ── autostart ───────────────────────────────────────────────────── */
static void wm_run_autostart(void) {
const char *home = getenv("HOME");
if (!home) return;
char path[512];
snprintf(path, sizeof(path), "%s/.config/orionos/autostart", home);
FILE *f = fopen(path, "r");
if (!f) return;
char line[512];
while (fgets(line, sizeof(line), f)) {
size_t len = strlen(line);
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r' ||
line[len-1] == ' ' || line[len-1] == '\t'))
line[--len] = '\0';
if (len == 0 || line[0] == '#') continue;
char cmd[560];
snprintf(cmd, sizeof(cmd), "%s &", line);
system(cmd);
}
fclose(f);
}
/* ── hot corners ─────────────────────────────────────────────────── */
static void hot_corner_check(WM *wm) {
Window root_ret, child;
int root_x, root_y, win_x, win_y;
unsigned int mask;
XQueryPointer(wm->dpy, wm->root, &root_ret, &child,
&root_x, &root_y, &win_x, &win_y, &mask);
int sw = DisplayWidth(wm->dpy, wm->screen);
int sh = DisplayHeight(wm->dpy, wm->screen);
int corner = 0;
if (root_x <= 1 && root_y <= 1) corner = 1; /* TL: Exposé */
else if (root_x >= sw - DESKBAR_W - 2 && root_y <= 1) corner = 2; /* TR: show desktop */
else if (root_x <= 1 && root_y >= sh - 2) corner = 3; /* BL: launcher */
else if (root_x >= sw - DESKBAR_W - 2 && root_y >= sh - 2) corner = 4; /* BR: lock */
if (corner == wm->hot_corner_last) return;
wm->hot_corner_last = corner;
if (corner == 0) return; /* just left a corner, no action needed */
switch (corner) {
case 1: /* top-left → Exposé */
if (!wm->expose_active) expose_show(wm);
break;
case 2: /* top-right → show desktop (minimize all on current desktop) */
if (wm->expose_active) { expose_hide(wm, None); break; }
for (Client *c = wm->clients; c; c = c->next)
if (!c->minimized && c->desktop == wm->cur_desktop)
client_minimize(wm, c);
break;
case 3: /* bottom-left → launcher */
system("orionlauncher &");
break;
case 4: /* bottom-right → lock screen */
system("orionlock &");
break;
}
}
static int x_error_handler(Display *dpy, XErrorEvent *e) {
if (e->error_code == BadWindow || e->error_code == BadAccess)
return 0;
@@ -115,6 +177,17 @@ void wm_init(WM *wm) {
{ XK_3, Mod4Mask | ShiftMask },
{ XK_4, Mod4Mask | ShiftMask },
{ XK_Escape, 0 },
/* XF86 media keys */
{ XF86XK_AudioRaiseVolume, 0 },
{ XF86XK_AudioLowerVolume, 0 },
{ XF86XK_AudioMute, 0 },
{ XF86XK_AudioPlay, 0 },
{ XF86XK_AudioNext, 0 },
{ XF86XK_AudioPrev, 0 },
{ XF86XK_MonBrightnessUp, 0 },
{ XF86XK_MonBrightnessDown, 0 },
/* Super+V: clipboard manager */
{ XK_v, Mod4Mask },
};
for (int i = 0; i < (int)(sizeof(grabs)/sizeof(grabs[0])); i++) {
KeyCode kc = XKeysymToKeycode(wm->dpy, grabs[i].sym);
@@ -138,28 +211,44 @@ void wm_init(WM *wm) {
XSync(wm->dpy, False);
fprintf(stderr, "orionwm: started\n");
wm_run_autostart();
}
void wm_run(WM *wm) {
int xfd = ConnectionNumber(wm->dpy);
XEvent e;
for (;;) {
XNextEvent(wm->dpy, &e);
switch (e.type) {
case MapRequest: on_map_request(wm, &e.xmaprequest); break;
case UnmapNotify: on_unmap_notify(wm, &e.xunmap); break;
case DestroyNotify: on_destroy_notify(wm, &e.xdestroywindow); break;
case ConfigureRequest: on_configure_request(wm, &e.xconfigurerequest); break;
case Expose: on_expose(wm, &e.xexpose); break;
case ButtonPress: on_button_press(wm, &e.xbutton); break;
case ButtonRelease: on_button_release(wm, &e.xbutton); break;
case MotionNotify: on_motion_notify(wm, &e.xmotion); break;
case KeyPress: on_key_press(wm, &e.xkey); break;
case KeyRelease: on_key_release(wm, &e.xkey); break;
case EnterNotify: on_enter_notify(wm, &e.xcrossing); break;
case ClientMessage: on_client_message(wm, &e.xclient); break;
case PropertyNotify: on_property_notify(wm, &e.xproperty); break;
default: break;
/* drain all pending X events */
while (XPending(wm->dpy)) {
XNextEvent(wm->dpy, &e);
switch (e.type) {
case MapRequest: on_map_request(wm, &e.xmaprequest); break;
case UnmapNotify: on_unmap_notify(wm, &e.xunmap); break;
case DestroyNotify: on_destroy_notify(wm, &e.xdestroywindow); break;
case ConfigureRequest: on_configure_request(wm, &e.xconfigurerequest); break;
case Expose: on_expose(wm, &e.xexpose); break;
case ButtonPress: on_button_press(wm, &e.xbutton); break;
case ButtonRelease: on_button_release(wm, &e.xbutton); break;
case MotionNotify: on_motion_notify(wm, &e.xmotion); break;
case KeyPress: on_key_press(wm, &e.xkey); break;
case KeyRelease: on_key_release(wm, &e.xkey); break;
case EnterNotify: on_enter_notify(wm, &e.xcrossing); break;
case ClientMessage: on_client_message(wm, &e.xclient); break;
case PropertyNotify: on_property_notify(wm, &e.xproperty); break;
default: break;
}
}
/* poll hot corners every 200ms */
hot_corner_check(wm);
fd_set fds;
FD_ZERO(&fds);
FD_SET(xfd, &fds);
struct timeval tv = { 0, 200000 };
select(xfd + 1, &fds, NULL, NULL, &tv);
}
}