diff --git a/sowm/sowm/.gitignore b/sowm/sowm/.gitignore new file mode 100644 index 0000000..db4f001 --- /dev/null +++ b/sowm/sowm/.gitignore @@ -0,0 +1,3 @@ +sowm +sowm.o +config.h diff --git a/sowm/sowm/LICENSE.md b/sowm/sowm/LICENSE.md new file mode 100644 index 0000000..38cf607 --- /dev/null +++ b/sowm/sowm/LICENSE.md @@ -0,0 +1,33 @@ +MIT/X Consortium License + +- © 2019- Dylan Araps +- © 2006-2011 Anselm R Garbe +- © 2007-2011 Peter Hartlich +- © 2010-2011 Connor Lane Smith +- © 2006-2009 Jukka Salmi +- © 2007-2009 Premysl Hruby +- © 2007-2009 Szabolcs Nagy +- © 2007-2009 Christof Musik +- © 2009 Mate Nagy +- © 2007-2008 Enno Gottox Boland +- © 2008 Martin Hurton +- © 2008 Neale Pickett +- © 2006-2007 Sander van Dijk + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/sowm/sowm/Makefile b/sowm/sowm/Makefile new file mode 100644 index 0000000..20f3734 --- /dev/null +++ b/sowm/sowm/Makefile @@ -0,0 +1,24 @@ +CFLAGS += -std=c99 -Wall -Wextra -pedantic -Wold-style-declaration +CFLAGS += -Wmissing-prototypes -Wno-unused-parameter +PREFIX ?= /usr +BINDIR ?= $(PREFIX)/bin +CC ?= gcc + +all: sowm + +config.h: + cp config.def.h config.h + +sowm: sowm.c sowm.h config.h Makefile + $(CC) -O3 $(CFLAGS) -o $@ $< -lX11 $(LDFLAGS) + +install: all + install -Dm755 sowm $(DESTDIR)$(BINDIR)/sowm + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/sowm + +clean: + rm -f sowm *.o + +.PHONY: all install uninstall clean diff --git a/sowm/sowm/README.md b/sowm/sowm/README.md new file mode 100644 index 0000000..e0aa3e9 --- /dev/null +++ b/sowm/sowm/README.md @@ -0,0 +1,98 @@ +# sowm (*~~Simple~~ Shitty Opinionated Window Manager*) + + + +An itsy bitsy floating window manager (*220~ sloc!*). + +- Floating only. +- Fullscreen toggle. +- Window centering. +- Mix of mouse and keyboard workflow. +- Focus with cursor. +- Rounded corners (*[through patch](https://github.com/dylanaraps/sowm/pull/58)*) +- Titlebars (*[through patch](https://github.com/dylanaraps/sowm/pull/57)*) + + + +- Alt-Tab window focusing. +- All windows die on exit. +- No window borders. +- [No ICCCM](https://web.archive.org/web/20190617214524/https://raw.githubusercontent.com/kfish/xsel/1a1c5edf0dc129055f7764c666da2dd468df6016/rant.txt). +- No EWMH. +- etc etc etc + + +
+ +Patches available here: https://github.com/dylanaraps/sowm/pulls + +## Default Keybindings + +**Window Management** + +| combo | action | +| -------------------------- | -----------------------| +| `Mouse` | focus under cursor | +| `MOD4` + `Left Mouse` | move window | +| `MOD4` + `Right Mouse` | resize window | +| `MOD4` + `f` | maximize toggle | +| `MOD4` + `c` | center window | +| `MOD4` + `q` | kill window | +| `MOD4` + `1-6` | desktop swap | +| `MOD4` + `Shift` +`1-6` | send window to desktop | +| `MOD1` + `TAB` (*alt-tab*) | focus cycle | + +**Programs** + +| combo | action | program | +| ------------------------ | ---------------- | -------------- | +| `MOD4` + `Return` | terminal | `st` | +| `MOD4` + `d` | dmenu | `dmenu_run` | +| `MOD4` + `p` | scrot | `scr` | +| `MOD4` + `w` | wallpaper cycler | `bud` | +| `XF86_AudioLowerVolume` | volume down | `amixer` | +| `XF86_AudioRaiseVolume` | volume up | `amixer` | +| `XF86_AudioMute` | volume toggle | `amixer` | +| `XF86_MonBrightnessUp` | brightness up | `bri` | +| `XF86_MonBrightnessDown` | brightness down | `bri` | + + +## Dependencies + +- `xlib` (*usually `libX11`*). + + +## Installation + +1) Copy `config.def.h` to `config.h` and modify it to suit your needs. +2) Run `make` to build `sowm`. +3) Copy it to your path or run `make install`. + - `DESTDIR` and `PREFIX` are supported. +4) (Optional) Apply patch with `git apply patches/patch-name` + - In case of applying multiple patches, it has to be done **manually**. + +If you are using GDM, save the following to `/usr/share/xsessions/sowm.desktop`. It is still recommended to start `sowm` from `.xinitrc` or through +[your own xinit implementation](https://github.com/dylanaraps/bin/blob/dfd9a9ff4555efb1cc966f8473339f37d13698ba/x). + +``` +[Desktop Entry] +Name=sowm +Comment=This session runs sowm as desktop manager +Exec=sowm +Type=Application +``` + + +## Thanks + +- [2bwm](https://github.com/venam/2bwm) +- [SmallWM](https://github.com/adamnew123456/SmallWM) +- [berry](https://github.com/JLErvin/berry) +- [catwm](https://github.com/pyknite/catwm) +- [dminiwm](https://github.com/moetunes/dminiwm) +- [dwm](https://dwm.suckless.org) +- [monsterwm](https://github.com/c00kiemon5ter/monsterwm) +- [openbox](https://github.com/danakj/openbox) +- [possum-wm](https://github.com/duckinator/possum-wm) +- [swm](https://github.com/dcat/swm) +- [tinywm](http://incise.org/tinywm.html) diff --git a/sowm/sowm/config.def.h b/sowm/sowm/config.def.h new file mode 100644 index 0000000..cae2009 --- /dev/null +++ b/sowm/sowm/config.def.h @@ -0,0 +1,49 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define MOD Mod4Mask + +const char* menu[] = {"dmenu_run", 0}; +const char* term[] = {"st", 0}; +const char* scrot[] = {"scr", 0}; +const char* briup[] = {"bri", "10", "+", 0}; +const char* bridown[] = {"bri", "10", "-", 0}; +const char* voldown[] = {"amixer", "sset", "Master", "5%-", 0}; +const char* volup[] = {"amixer", "sset", "Master", "5%+", 0}; +const char* volmute[] = {"amixer", "sset", "Master", "toggle", 0}; +const char* colors[] = {"bud", "/home/goldie/Pictures/Wallpapers", 0}; + +static struct key keys[] = { + {MOD, XK_q, win_kill, {0}}, + {MOD, XK_c, win_center, {0}}, + {MOD, XK_f, win_fs, {0}}, + + {Mod1Mask, XK_Tab, win_next, {0}}, + {Mod1Mask|ShiftMask, XK_Tab, win_prev, {0}}, + + {MOD, XK_d, run, {.com = menu}}, + {MOD, XK_w, run, {.com = colors}}, + {MOD, XK_p, run, {.com = scrot}}, + {MOD, XK_Return, run, {.com = term}}, + + {0, XF86XK_AudioLowerVolume, run, {.com = voldown}}, + {0, XF86XK_AudioRaiseVolume, run, {.com = volup}}, + {0, XF86XK_AudioMute, run, {.com = volmute}}, + {0, XF86XK_MonBrightnessUp, run, {.com = briup}}, + {0, XF86XK_MonBrightnessDown, run, {.com = bridown}}, + + {MOD, XK_1, ws_go, {.i = 1}}, + {MOD|ShiftMask, XK_1, win_to_ws, {.i = 1}}, + {MOD, XK_2, ws_go, {.i = 2}}, + {MOD|ShiftMask, XK_2, win_to_ws, {.i = 2}}, + {MOD, XK_3, ws_go, {.i = 3}}, + {MOD|ShiftMask, XK_3, win_to_ws, {.i = 3}}, + {MOD, XK_4, ws_go, {.i = 4}}, + {MOD|ShiftMask, XK_4, win_to_ws, {.i = 4}}, + {MOD, XK_5, ws_go, {.i = 5}}, + {MOD|ShiftMask, XK_5, win_to_ws, {.i = 5}}, + {MOD, XK_6, ws_go, {.i = 6}}, + {MOD|ShiftMask, XK_6, win_to_ws, {.i = 6}}, +}; + +#endif diff --git a/sowm/sowm/sowm.c b/sowm/sowm/sowm.c new file mode 100644 index 0000000..96f8340 --- /dev/null +++ b/sowm/sowm/sowm.c @@ -0,0 +1,289 @@ +// sowm - An itsy bitsy floating window manager. + +#include +#include +#include +#include +#include +#include +#include + +#include "sowm.h" + +static client *list = {0}, *ws_list[10] = {0}, *cur; +static int ws = 1, sw, sh, wx, wy, numlock = 0; +static unsigned int ww, wh; + +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" + +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)); + + 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))); +} + +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); +} + +void win_center(const Arg arg) { + if (!cur) return; + + win_size(cur->w, &(int){0}, &(int){0}, &ww, &wh); + XMoveWindow(d, cur->w, (sw - ww) / 2, (sh - wh) / 2); +} + +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); + 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 + }); +} + +void map_request(XEvent *e) { + Window w = e->xmaprequest.window; + + XSelectInput(d, w, StructureNotifyMask|EnterWindowMask); + win_size(w, &wx, &wy, &ww, &wh); + win_add(w); + cur = list->prev; + + 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); + sh = XDisplayHeight(d, s); + + 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); +} diff --git a/sowm/sowm/sowm.h b/sowm/sowm/sowm.h new file mode 100644 index 0000000..b059f06 --- /dev/null +++ b/sowm/sowm/sowm.h @@ -0,0 +1,58 @@ +#include + +#define win (client *t=0, *c=list; c && t!=list->prev; t=c, c=c->next) +#define ws_save(W) ws_list[W] = list +#define ws_sel(W) list = ws_list[ws = W] +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define win_size(W, gx, gy, gw, gh) \ + XGetGeometry(d, W, &(Window){0}, gx, gy, gw, gh, \ + &(unsigned int){0}, &(unsigned int){0}) + +// Taken from DWM. Many thanks. https://git.suckless.org/dwm +#define mod_clean(mask) (mask & ~(numlock|LockMask) & \ + (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) + +typedef struct { + const char** com; + const int i; + const Window w; +} Arg; + +struct key { + unsigned int mod; + KeySym keysym; + void (*function)(const Arg arg); + const Arg arg; +}; + +typedef struct client { + struct client *next, *prev; + int f, wx, wy; + unsigned int ww, wh; + Window w; +} client; + +void button_press(XEvent *e); +void button_release(XEvent *e); +void configure_request(XEvent *e); +void input_grab(Window root); +void key_press(XEvent *e); +void map_request(XEvent *e); +void mapping_notify(XEvent *e); +void notify_destroy(XEvent *e); +void notify_enter(XEvent *e); +void notify_motion(XEvent *e); +void run(const Arg arg); +void win_add(Window w); +void win_center(const Arg arg); +void win_del(Window w); +void win_fs(const Arg arg); +void win_focus(client *c); +void win_kill(const Arg arg); +void win_prev(const Arg arg); +void win_next(const Arg arg); +void win_to_ws(const Arg arg); +void ws_go(const Arg arg); + +static int xerror() { return 0; } diff --git a/sowm/startsowm.sh b/sowm/startsowm.sh new file mode 100755 index 0000000..fde2da5 --- /dev/null +++ b/sowm/startsowm.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +setxkbmap "us,ru" -option 'grp:win_space_toggle' +~/dotfiles/dwm/background.sh & +picom -b +dunst & + +while true; do + sowm 2> ~/.sowm.log +done +