Initial commit: SkyOS source code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
e641720a3c
30 files changed
+5836
No files matched your search
+302
@@ -0,0 +1,302 @@
|
||||
#include "wm.h"
|
||||
|
||||
/* ── helpers ─────────────────────────────────────────────────────────── */
|
||||
|
||||
static void send_delete(WM *wm, Window win) {
|
||||
XEvent ev = {0};
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = win;
|
||||
ev.xclient.message_type = wm->wm_protocols;
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = wm->wm_delete_window;
|
||||
ev.xclient.data.l[1] = CurrentTime;
|
||||
XSendEvent(wm->dpy, win, False, NoEventMask, &ev);
|
||||
}
|
||||
|
||||
static void set_cursor(WM *wm, Client *c, int x, int y) {
|
||||
Cursor cur;
|
||||
if (!c->maximized && frame_hit_resize(c, x, y)) cur = wm->cur_resize;
|
||||
else if (y < TITLE_HEIGHT) cur = wm->cur_move;
|
||||
else cur = wm->cur_normal;
|
||||
XDefineCursor(wm->dpy, c->frame, cur);
|
||||
}
|
||||
|
||||
/* ── snap on drag release ────────────────────────────────────────────── */
|
||||
|
||||
static void check_snap(WM *wm, Client *c) {
|
||||
int sw = DisplayWidth(wm->dpy, wm->screen);
|
||||
int sh = DisplayHeight(wm->dpy, wm->screen);
|
||||
|
||||
if (c->x <= SNAP_DIST) {
|
||||
if (c->y <= SNAP_DIST) {
|
||||
client_maximize(wm, c);
|
||||
} else {
|
||||
client_snap_left(wm, c);
|
||||
}
|
||||
} else if (c->x + (int)(c->w + BORDER_W * 2) >= sw - 185 - SNAP_DIST) {
|
||||
client_snap_right(wm, c);
|
||||
} else if (c->y <= SNAP_DIST) {
|
||||
client_maximize(wm, c);
|
||||
} else {
|
||||
(void)sh;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── event handlers ──────────────────────────────────────────────────── */
|
||||
|
||||
void on_map_request(WM *wm, XMapRequestEvent *e) {
|
||||
client_add(wm, e->window);
|
||||
}
|
||||
|
||||
void on_unmap_notify(WM *wm, XUnmapEvent *e) {
|
||||
if (e->send_event) return;
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
if (c && !c->minimized) client_remove(wm, e->window);
|
||||
}
|
||||
|
||||
void on_destroy_notify(WM *wm, XDestroyWindowEvent *e) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
if (!c) c = client_find_by_frame(wm, e->window);
|
||||
if (c) client_remove(wm, c->win);
|
||||
}
|
||||
|
||||
void on_expose(WM *wm, XExposeEvent *e) {
|
||||
if (e->count != 0) return;
|
||||
if (wm->menu_visible && e->window == wm->menu_win) {
|
||||
menu_draw(wm);
|
||||
return;
|
||||
}
|
||||
Client *c = client_find_by_frame(wm, e->window);
|
||||
if (c && !c->minimized) frame_draw(wm, c);
|
||||
}
|
||||
|
||||
void on_configure_request(WM *wm, XConfigureRequestEvent *e) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
XWindowChanges wc;
|
||||
wc.x = e->x; wc.y = e->y;
|
||||
wc.width = e->width; wc.height = e->height;
|
||||
wc.border_width = e->border_width;
|
||||
wc.sibling = e->above; wc.stack_mode = e->detail;
|
||||
if (c && !c->maximized) {
|
||||
c->w = e->width; c->h = e->height;
|
||||
XResizeWindow(wm->dpy, c->frame,
|
||||
c->w + BORDER_W*2, c->h + TITLE_HEIGHT + BORDER_W);
|
||||
}
|
||||
XConfigureWindow(wm->dpy, e->window, e->value_mask, &wc);
|
||||
}
|
||||
|
||||
void on_enter_notify(WM *wm, XCrossingEvent *e) {
|
||||
Client *c = client_find_by_frame(wm, e->window);
|
||||
if (c && !c->minimized) set_cursor(wm, c, e->x, e->y);
|
||||
}
|
||||
|
||||
void on_client_message(WM *wm, XClientMessageEvent *e) {
|
||||
Atom net_active = XInternAtom(wm->dpy, "_NET_ACTIVE_WINDOW", False);
|
||||
if (e->message_type == net_active) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
if (c) client_restore(wm, c);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called when a managed window changes a property (title, etc.) */
|
||||
void on_property_notify(WM *wm, XPropertyEvent *e) {
|
||||
Client *c = client_find_by_win(wm, e->window);
|
||||
if (!c || c->minimized) return;
|
||||
|
||||
Atom wm_name = XInternAtom(wm->dpy, "WM_NAME", False);
|
||||
Atom net_wm_name = XInternAtom(wm->dpy, "_NET_WM_NAME", False);
|
||||
Atom utf8 = XInternAtom(wm->dpy, "UTF8_STRING", False);
|
||||
|
||||
if (e->atom != wm_name && e->atom != net_wm_name) return;
|
||||
|
||||
/* re-fetch title */
|
||||
char new_title[256] = {0};
|
||||
{
|
||||
Atom type; int fmt; unsigned long n, after;
|
||||
unsigned char *data = NULL;
|
||||
if (XGetWindowProperty(wm->dpy, c->win, net_wm_name, 0, 256, False,
|
||||
utf8, &type, &fmt, &n, &after, &data)
|
||||
== Success && data && n > 0) {
|
||||
snprintf(new_title, 255, "%.*s", (int)n, data);
|
||||
XFree(data);
|
||||
} else {
|
||||
if (data) XFree(data);
|
||||
char *name = NULL;
|
||||
if (XFetchName(wm->dpy, c->win, &name) && name) {
|
||||
strncpy(new_title, name, 255);
|
||||
XFree(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_title[0] && strcmp(new_title, c->title) != 0) {
|
||||
strncpy(c->title, new_title, 255);
|
||||
frame_draw(wm, c);
|
||||
}
|
||||
}
|
||||
|
||||
void on_button_press(WM *wm, XButtonEvent *e) {
|
||||
/* menu window */
|
||||
if (wm->menu_visible && e->window == wm->menu_win) {
|
||||
if (e->button == Button1) menu_click(wm, e->x, e->y);
|
||||
else menu_hide(wm);
|
||||
return;
|
||||
}
|
||||
/* right-click root → menu */
|
||||
if (e->window == wm->root && e->button == Button3) {
|
||||
menu_show(wm, e->x_root, e->y_root);
|
||||
return;
|
||||
}
|
||||
if (wm->menu_visible) { menu_hide(wm); return; }
|
||||
|
||||
Client *c = client_find_by_frame(wm, e->window);
|
||||
if (!c || c->minimized) return;
|
||||
|
||||
client_focus(wm, c);
|
||||
if (e->button != Button1) return;
|
||||
|
||||
if (frame_hit_close(c, e->x, e->y)) {
|
||||
send_delete(wm, c->win);
|
||||
return;
|
||||
}
|
||||
if (frame_hit_maximize(c, e->x, e->y)) {
|
||||
client_toggle_maximize(wm, c);
|
||||
return;
|
||||
}
|
||||
if (frame_hit_minimize(c, e->x, e->y)) {
|
||||
client_minimize(wm, c);
|
||||
return;
|
||||
}
|
||||
if (!c->maximized && frame_hit_resize(c, e->x, e->y)) {
|
||||
wm->drag_mode = DRAG_RESIZE;
|
||||
wm->drag_frame = c->frame;
|
||||
wm->drag_start_x = e->x_root;
|
||||
wm->drag_start_y = e->y_root;
|
||||
wm->drag_win_w = c->w;
|
||||
wm->drag_win_h = c->h;
|
||||
XGrabPointer(wm->dpy, c->frame, False,
|
||||
ButtonReleaseMask | PointerMotionMask,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
None, wm->cur_resize, CurrentTime);
|
||||
return;
|
||||
}
|
||||
if (e->y < TITLE_HEIGHT) {
|
||||
/* double-click title bar → maximize toggle */
|
||||
if (e->time - c->last_click_time < DBL_CLICK_MS) {
|
||||
client_toggle_maximize(wm, c);
|
||||
c->last_click_time = 0;
|
||||
return;
|
||||
}
|
||||
c->last_click_time = e->time;
|
||||
|
||||
if (c->maximized) return; /* don't drag a maximized window */
|
||||
|
||||
wm->drag_mode = DRAG_MOVE;
|
||||
wm->drag_frame = c->frame;
|
||||
wm->drag_start_x = e->x_root;
|
||||
wm->drag_start_y = e->y_root;
|
||||
wm->drag_win_x = c->x;
|
||||
wm->drag_win_y = c->y;
|
||||
XGrabPointer(wm->dpy, c->frame, False,
|
||||
ButtonReleaseMask | PointerMotionMask,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
None, wm->cur_move, CurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
void on_button_release(WM *wm, XButtonEvent *e) {
|
||||
(void)e;
|
||||
if (wm->drag_mode == DRAG_MOVE) {
|
||||
/* snap check after move */
|
||||
Client *c = client_find_by_frame(wm, wm->drag_frame);
|
||||
if (c) check_snap(wm, c);
|
||||
}
|
||||
if (wm->drag_mode != DRAG_NONE) {
|
||||
wm->drag_mode = DRAG_NONE;
|
||||
XUngrabPointer(wm->dpy, CurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
void on_motion_notify(WM *wm, XMotionEvent *e) {
|
||||
if (wm->menu_visible && e->window == wm->menu_win) {
|
||||
int idx = menu_item_at(e->y);
|
||||
if (idx != wm->menu_hovered) {
|
||||
wm->menu_hovered = idx;
|
||||
menu_draw(wm);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (wm->drag_mode == DRAG_NONE) {
|
||||
Client *c = client_find_by_frame(wm, e->window);
|
||||
if (c) set_cursor(wm, c, e->x, e->y);
|
||||
return;
|
||||
}
|
||||
Client *c = client_find_by_frame(wm, wm->drag_frame);
|
||||
if (!c) return;
|
||||
int dx = e->x_root - wm->drag_start_x;
|
||||
int dy = e->y_root - wm->drag_start_y;
|
||||
if (wm->drag_mode == DRAG_MOVE) {
|
||||
c->x = wm->drag_win_x + dx;
|
||||
c->y = wm->drag_win_y + dy;
|
||||
if (c->y < 0) c->y = 0; /* don't drag above screen top */
|
||||
XMoveWindow(wm->dpy, c->frame, c->x, c->y);
|
||||
} else {
|
||||
int nw = (int)wm->drag_win_w + dx;
|
||||
int nh = (int)wm->drag_win_h + dy;
|
||||
if (nw < MIN_WIN_W) nw = MIN_WIN_W;
|
||||
if (nh < MIN_WIN_H) nh = MIN_WIN_H;
|
||||
c->w = (unsigned)nw; c->h = (unsigned)nh;
|
||||
XResizeWindow(wm->dpy, c->frame, c->w+BORDER_W*2, c->h+TITLE_HEIGHT+BORDER_W);
|
||||
XResizeWindow(wm->dpy, c->win, c->w, c->h);
|
||||
frame_draw(wm, c);
|
||||
}
|
||||
}
|
||||
|
||||
void on_key_press(WM *wm, XKeyEvent *e) {
|
||||
KeySym sym = XLookupKeysym(e, 0);
|
||||
|
||||
/* Alt+F4 — close focused window */
|
||||
if ((e->state & Mod1Mask) && sym == XK_F4 && wm->focused)
|
||||
send_delete(wm, wm->focused->win);
|
||||
|
||||
/* Alt+Tab — cycle windows */
|
||||
if ((e->state & Mod1Mask) && sym == XK_Tab && wm->clients) {
|
||||
Client *start = wm->focused ? wm->focused->next : wm->clients;
|
||||
Client *n = start;
|
||||
while (n && n->minimized) n = n->next;
|
||||
if (!n) { n = wm->clients; while (n && n->minimized) n = n->next; }
|
||||
if (n && n != wm->focused) client_focus(wm, n);
|
||||
}
|
||||
|
||||
/* Alt+F9 — minimize */
|
||||
if ((e->state & Mod1Mask) && sym == XK_F9 && wm->focused)
|
||||
client_minimize(wm, wm->focused);
|
||||
|
||||
/* Alt+F10 — maximize toggle */
|
||||
if ((e->state & Mod1Mask) && sym == XK_F10 && wm->focused)
|
||||
client_toggle_maximize(wm, wm->focused);
|
||||
|
||||
/* Alt+F1 — launcher */
|
||||
if ((e->state & Mod1Mask) && sym == XK_F1)
|
||||
system("skylauncher &");
|
||||
|
||||
/* Super+Left — snap left */
|
||||
if ((e->state & Mod4Mask) && sym == XK_Left && wm->focused)
|
||||
client_snap_left(wm, wm->focused);
|
||||
|
||||
/* Super+Right — snap right */
|
||||
if ((e->state & Mod4Mask) && sym == XK_Right && wm->focused)
|
||||
client_snap_right(wm, wm->focused);
|
||||
|
||||
/* Super+Up — maximize */
|
||||
if ((e->state & Mod4Mask) && sym == XK_Up && wm->focused)
|
||||
client_maximize(wm, wm->focused);
|
||||
|
||||
/* Super+Down — unmaximize / restore */
|
||||
if ((e->state & Mod4Mask) && sym == XK_Down && wm->focused)
|
||||
client_unmaximize(wm, wm->focused);
|
||||
|
||||
/* Escape — hide menu */
|
||||
if (sym == XK_Escape && wm->menu_visible)
|
||||
menu_hide(wm);
|
||||
}
|
||||
Reference in new issue
Block a user