This repository has been archived on 2021-03-07. You can view files and clone it, but cannot push or open issues or pull requests.
sowm/sowm.c

374 lines
9.6 KiB
C

// sowm - An itsy bitsy floating window manager.
#include <X11/Xlib.h>
#include <X11/XF86keysym.h>
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/extensions/Xinerama.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <stdbool.h>
#include "sowm.h"
static client *list = {0}, *ws_list[NUM_WS] = {0}, *cur;
static int ws = 1, sw, sh, wx, wy, numlock = 0, monitors;
static unsigned int ww, wh;
static int s;
static Display *d;
static XButtonEvent mouse;
static Window root;
static void (*events[LASTEvent])(XEvent *e) = {
[ButtonPress] = button_press,
[ButtonRelease] = button_release,
[ConfigureRequest] = configure_request,
[KeyPress] = key_press,
[MapRequest] = map_request,
[MappingNotify] = mapping_notify,
[DestroyNotify] = notify_destroy,
[EnterNotify] = notify_enter,
[MotionNotify] = notify_motion
};
#include "config.h"
unsigned long getcolor(const char *col) {
Colormap m = DefaultColormap(d, s);
XColor c;
return (!XAllocNamedColor(d, m, col, &c, &c))?0:c.pixel;
}
void wm_exit(void) {
exit(0);
}
void win_move(const Arg arg) {
int r = arg.com[0][0] == 'r';
char m = arg.com[1][0];
win_size(cur->w, &wx, &wy, &ww, &wh);
XMoveResizeWindow(d, cur->w, \
wx + (r ? 0 : m == 'e' ? arg.i : m == 'w' ? -arg.i : 0),
wy + (r ? 0 : m == 'n' ? -arg.i : m == 's' ? arg.i : 0),
MAX(10, ww + (r ? m == 'e' ? arg.i : m == 'w' ? -arg.i : 0 : 0)),
MAX(10, wh + (r ? m == 'n' ? -arg.i : m == 's' ? arg.i : 0 : 0)));
}
void win_half(const Arg arg) {
char m = arg.com[0][0];
win_size(cur->w, &wx, &wy, &ww, &wh);
XMoveResizeWindow(d, cur->w, \
(m == 'w' ? (unsigned int)wx : m == 'e' ? (wx + ww / 2) : (unsigned int)wx),
(m == 'n' ? (unsigned int)wy : m == 's' ? (wy + wh / 2) : (unsigned int)wy),
(m == 'w' ? (ww / 2) : m == 'e' ? (ww / 2) : ww),
(m == 'n' ? (wh / 2) : m == 's' ? (wh / 2) : wh));
}
void win_focus(client *c) {
cur = c;
XSetInputFocus(d, cur->w, RevertToParent, CurrentTime);
}
void notify_destroy(XEvent *e) {
win_del(e->xdestroywindow.window);
if (list) win_focus(list->prev);
}
void notify_enter(XEvent *e) {
while(XCheckTypedEvent(d, EnterNotify, e));
while(XCheckTypedWindowEvent(d, mouse.subwindow, MotionNotify, e));
for win if (c->w == e->xcrossing.window) win_focus(c);
}
void notify_motion(XEvent *e) {
if (!mouse.subwindow || cur->f) return;
while(XCheckTypedEvent(d, MotionNotify, e));
int xd = e->xbutton.x_root - mouse.x_root;
int yd = e->xbutton.y_root - mouse.y_root;
XMoveResizeWindow(d, mouse.subwindow,
wx + (mouse.button == 1 ? xd : 0),
wy + (mouse.button == 1 ? yd : 0),
MAX(1, ww + (mouse.button == 3 ? xd : 0)),
MAX(1, wh + (mouse.button == 3 ? yd : 0)));
win_size(cur->w, &cur->wx, &cur->wx, &cur->ww, &cur->wh);
}
void key_press(XEvent *e) {
KeySym keysym = XkbKeycodeToKeysym(d, e->xkey.keycode, 0, 0);
for (unsigned int i=0; i < sizeof(keys)/sizeof(*keys); ++i)
if (keys[i].keysym == keysym &&
mod_clean(keys[i].mod) == mod_clean(e->xkey.state))
keys[i].function(keys[i].arg);
}
void button_press(XEvent *e) {
if (!e->xbutton.subwindow) return;
win_size(e->xbutton.subwindow, &wx, &wy, &ww, &wh);
XRaiseWindow(d, e->xbutton.subwindow);
mouse = e->xbutton;
}
void button_release(XEvent *e) {
mouse.subwindow = 0;
}
void win_add(Window w) {
client *c;
if (!(c = (client *) calloc(1, sizeof(client))))
exit(1);
c->w = w;
if (list) {
list->prev->next = c;
c->prev = list->prev;
list->prev = c;
c->next = list;
} else {
list = c;
list->prev = list->next = list;
}
ws_save(ws);
}
void win_del(Window w) {
client *x = 0;
for win if (c->w == w) x = c;
if (!list || !x) return;
if (x->prev == x) list = 0;
if (list == x) list = x->next;
if (x->next) x->next->prev = x->prev;
if (x->prev) x->prev->next = x->next;
free(x);
ws_save(ws);
}
void win_kill(const Arg arg) {
if (cur) XKillClient(d, cur->w);
}
int multimonitor_action (int action) { /* 0 -> center, 1 -> fs */
if (!XineramaIsActive(d)) return 1;
XineramaScreenInfo *si = XineramaQueryScreens(d, &monitors);
for (int i = 0; i < monitors; i++) {
if ((cur->wx + (cur->ww/2) >= (unsigned int)si[i].x_org
&& cur->wx + (cur->ww/2) < (unsigned int)si[i].x_org + si[i].width)
&& ( cur->wy + (cur->wh/2) >= (unsigned int)si[i].y_org
&& cur->wy + (cur->wh/2) < (unsigned int)si[i].y_org + si[i].height)) {
if (action)
XMoveResizeWindow(d, cur->w,
si[i].x_org, si[i].y_org,
si[i].width - 2*BORDER_WIDTH,
si[i].height - 2*BORDER_WIDTH);
else
XMoveWindow(d, cur->w,
si[i].x_org + ((si[i].width - ww)/2),
si[i].y_org + ((si[i].height -wh)/2));
break;
}
}
return 0;
}
void win_center(const Arg arg) {
if (!cur) return;
win_size(cur->w, &(int){0}, &(int){0}, &ww, &wh);
if (multimonitor_action(0)) {
XMoveWindow(d, cur->w, (sw - ww) / 2, (sh - wh) / 2);
}
win_size(cur->w, &cur->wx, &cur->wy, &cur->ww, &cur->wh);
}
void win_fs(const Arg arg) {
if (!cur) return;
if ((cur->f = cur->f ? 0 : 1)) {
win_size(cur->w, &cur->wx, &cur->wy, &cur->ww, &cur->wh);
if(multimonitor_action(1))
XMoveResizeWindow(d, cur->w, 0, 0, sw, sh);
} else {
XMoveResizeWindow(d, cur->w, cur->wx, cur->wy, cur->ww, cur->wh);
}
}
void win_to_ws(const Arg arg) {
int tmp = ws;
if (arg.i == tmp) return;
ws_sel(arg.i);
win_add(cur->w);
ws_save(arg.i);
ws_sel(tmp);
win_del(cur->w);
XUnmapWindow(d, cur->w);
ws_save(tmp);
if (list) win_focus(list);
}
void win_prev(const Arg arg) {
if (!cur) return;
XRaiseWindow(d, cur->prev->w);
win_focus(cur->prev);
}
void win_next(const Arg arg) {
if (!cur) return;
XRaiseWindow(d, cur->next->w);
win_focus(cur->next);
}
void ws_go(const Arg arg) {
int tmp = ws;
if (arg.i == ws) return;
ws_save(ws);
ws_sel(arg.i);
for win XMapWindow(d, c->w);
ws_sel(tmp);
for win XUnmapWindow(d, c->w);
ws_sel(arg.i);
if (list) win_focus(list); else cur = 0;
}
void configure_request(XEvent *e) {
XConfigureRequestEvent *ev = &e->xconfigurerequest;
XConfigureWindow(d, ev->window, ev->value_mask, &(XWindowChanges) {
.x = ev->x,
.y = ev->y,
.width = ev->width,
.height = ev->height,
.sibling = ev->above,
.stack_mode = ev->detail
});
}
bool exists_win(Window w) {
int tmp = ws;
for (int i = 0; i < NUM_WS; ++i) {
if (i == tmp) continue;
ws_sel(i);
for win if (c->w == w) {
ws_sel(tmp);
return true;
}
}
ws_sel(tmp);
return false;
}
void map_request(XEvent *e) {
Window w = e->xmaprequest.window;
if(exists_win(w)) return;
XSelectInput(d, w, StructureNotifyMask|EnterWindowMask);
win_size(w, &wx, &wy, &ww, &wh);
win_add(w);
cur = list->prev;
XSetWindowBorder(d, w, getcolor(BORDER_COLOR));
XConfigureWindow(d, w, CWBorderWidth, &(XWindowChanges){.border_width = BORDER_WIDTH});
if (wx + wy == 0) win_center((Arg){0});
XMapWindow(d, w);
win_focus(list->prev);
}
void mapping_notify(XEvent *e) {
XMappingEvent *ev = &e->xmapping;
if (ev->request == MappingKeyboard || ev->request == MappingModifier) {
XRefreshKeyboardMapping(ev);
input_grab(root);
}
}
void run(const Arg arg) {
if (fork()) return;
if (d) close(ConnectionNumber(d));
setsid();
execvp((char*)arg.com[0], (char**)arg.com);
}
void input_grab(Window root) {
unsigned int i, j, modifiers[] = {0, LockMask, numlock, numlock|LockMask};
XModifierKeymap *modmap = XGetModifierMapping(d);
KeyCode code;
for (i = 0; i < 8; i++)
for (int k = 0; k < modmap->max_keypermod; k++)
if (modmap->modifiermap[i * modmap->max_keypermod + k]
== XKeysymToKeycode(d, 0xff7f))
numlock = (1 << i);
XUngrabKey(d, AnyKey, AnyModifier, root);
for (i = 0; i < sizeof(keys)/sizeof(*keys); i++)
if ((code = XKeysymToKeycode(d, keys[i].keysym)))
for (j = 0; j < sizeof(modifiers)/sizeof(*modifiers); j++)
XGrabKey(d, code, keys[i].mod | modifiers[j], root,
True, GrabModeAsync, GrabModeAsync);
for (i = 1; i < 4; i += 2)
for (j = 0; j < sizeof(modifiers)/sizeof(*modifiers); j++)
XGrabButton(d, i, MOD | modifiers[j], root, True,
ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
GrabModeAsync, GrabModeAsync, 0, 0);
XFreeModifiermap(modmap);
}
int main(void) {
XEvent ev;
if (!(d = XOpenDisplay(0))) exit(1);
signal(SIGCHLD, SIG_IGN);
XSetErrorHandler(xerror);
int s = DefaultScreen(d);
root = RootWindow(d, s);
sw = XDisplayWidth(d, s) - (2*BORDER_WIDTH);
sh = XDisplayHeight(d, s) - (2*BORDER_WIDTH);
XSelectInput(d, root, SubstructureRedirectMask);
XDefineCursor(d, root, XCreateFontCursor(d, 68));
input_grab(root);
while (1 && !XNextEvent(d, &ev)) // 1 && will forever be here.
if (events[ev.type]) events[ev.type](&ev);
}