Compare commits

..

No commits in common. "master" and "0.2" have entirely different histories.
master ... 0.2

7 changed files with 616 additions and 254 deletions

View File

@ -1,24 +1,21 @@
CFLAGS += -std=c99 -Wall -Wextra -pedantic -Wold-style-declaration CFLAGS+= -std=c99 -Wall -Wextra -pedantic -Wno-deprecated-declarations
CFLAGS += -Wmissing-prototypes -Wno-unused-parameter LDADD+= -lX11
PREFIX ?= /usr LDFLAGS=
BINDIR ?= $(PREFIX)/bin PREFIX?= /usr
CC ?= gcc BINDIR?= $(PREFIX)/bin
all: sowm CC ?= gcc
all: config.h sowm
config.h: config.h:
cp config.def.h config.h cp config.def.h config.h
sowm: sowm.c sowm.h config.h Makefile sowm: sowm.o
$(CC) -O3 $(CFLAGS) -o $@ $< -lX11 $(LDFLAGS) $(CC) $(LDFLAGS) -O3 -o $@ $+ $(LDADD)
install: all install: all
install -Dm755 sowm $(DESTDIR)$(BINDIR)/sowm install -Dm 755 sowm $(DESTDIR)$(BINDIR)/sowm
uninstall:
rm -f $(DESTDIR)$(BINDIR)/sowm
clean: clean:
rm -f sowm *.o rm -f sowm *.o
.PHONY: all install uninstall clean

View File

@ -1,31 +1,29 @@
# sowm (*~~Simple~~ Shitty Opinionated Window Manager*) # sowm
<a href="https://user-images.githubusercontent.com/6799467/66687576-9747c200-ec72-11e9-947d-5b96753eab03.jpg"><img src="https://user-images.githubusercontent.com/6799467/66687576-9747c200-ec72-11e9-947d-5b96753eab03.jpg" width="43%" align="right"></a> <a href="https://user-images.githubusercontent.com/6799467/66687576-9747c200-ec72-11e9-947d-5b96753eab03.jpg"><img src="https://user-images.githubusercontent.com/6799467/66687576-9747c200-ec72-11e9-947d-5b96753eab03.jpg" width="43%" align="right"></a>
An itsy bitsy floating window manager (*220~ sloc!*). An itsy bitsy floating window manager (*270~ sloc / 24kb compiled!*).
- Floating only. - Floating only.
- Fullscreen toggle. - Fullscreen toggle.
- Window centering. - Window centering.
- Mix of mouse and keyboard workflow. - Mix of mouse and keyboard workflow.
- Focus with cursor. - Focus with cursor.
- Rounded corners (*[through patch](https://github.com/dylanaraps/sowm/pull/58)*) - Rounded corners (*[through patch](https://github.com/dylanaraps/sowm/blob/master/patches/sowm-rounded-corners.patch)*)
- Titlebars (*[through patch](https://github.com/dylanaraps/sowm/pull/57)*)
<a href="https://user-images.githubusercontent.com/6799467/66687814-8cd9f800-ec73-11e9-97b8-6ae77876bd1b.jpg"><img src="https://user-images.githubusercontent.com/6799467/66687814-8cd9f800-ec73-11e9-97b8-6ae77876bd1b.jpg" width="43%" align="right"></a> <a href="https://user-images.githubusercontent.com/6799467/66687814-8cd9f800-ec73-11e9-97b8-6ae77876bd1b.jpg"><img src="https://user-images.githubusercontent.com/6799467/66687814-8cd9f800-ec73-11e9-97b8-6ae77876bd1b.jpg" width="43%" align="right"></a>
- Alt-Tab window focusing. - Alt-Tab window focusing.
- All windows die on exit. - All windows die on exit.
- No window borders. - No borders.
- [No ICCCM](https://web.archive.org/web/20190617214524/https://raw.githubusercontent.com/kfish/xsel/1a1c5edf0dc129055f7764c666da2dd468df6016/rant.txt). - No bar support.
- No EWMH. - No ICCCM.
- No EMWH.
- etc etc etc - etc etc etc
<br> <br>
Patches available here: https://github.com/dylanaraps/sowm/pulls
## Default Keybindings ## Default Keybindings
**Window Management** **Window Management**
@ -38,8 +36,8 @@ Patches available here: https://github.com/dylanaraps/sowm/pulls
| `MOD4` + `f` | maximize toggle | | `MOD4` + `f` | maximize toggle |
| `MOD4` + `c` | center window | | `MOD4` + `c` | center window |
| `MOD4` + `q` | kill window | | `MOD4` + `q` | kill window |
| `MOD4` + `1-6` | desktop swap | | `MOD4` + `1-9` | desktop swap |
| `MOD4` + `Shift` +`1-6` | send window to desktop | | `MOD4` + `Shift` +`1-9` | send window to desktop |
| `MOD1` + `TAB` (*alt-tab*) | focus cycle | | `MOD1` + `TAB` (*alt-tab*) | focus cycle |
**Programs** **Programs**
@ -68,31 +66,18 @@ Patches available here: https://github.com/dylanaraps/sowm/pulls
2) Run `make` to build `sowm`. 2) Run `make` to build `sowm`.
3) Copy it to your path or run `make install`. 3) Copy it to your path or run `make install`.
- `DESTDIR` and `PREFIX` are supported. - `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 ## Thanks
- [2bwm](https://github.com/venam/2bwm) - 2bwm
- [SmallWM](https://github.com/adamnew123456/SmallWM) - SmallWM
- [berry](https://github.com/JLErvin/berry) - berry
- [catwm](https://github.com/pyknite/catwm) - catwm
- [dminiwm](https://github.com/moetunes/dminiwm) - dminiwm
- [dwm](https://dwm.suckless.org) - dwm
- [monsterwm](https://github.com/c00kiemon5ter/monsterwm) - monsterwm
- [openbox](https://github.com/danakj/openbox) - openbox
- [possum-wm](https://github.com/duckinator/possum-wm) - possumwm
- [swm](https://github.com/dcat/swm) - swm
- [tinywm](http://incise.org/tinywm.html) - tinywm

View File

@ -15,11 +15,9 @@ const char* colors[] = {"bud", "/home/goldie/Pictures/Wallpapers", 0};
static struct key keys[] = { static struct key keys[] = {
{MOD, XK_q, win_kill, {0}}, {MOD, XK_q, win_kill, {0}},
{MOD, XK_c, win_center, {0}}, {MOD, XK_c, win_center, {.w = 0}},
{MOD, XK_f, win_fs, {0}}, {MOD, XK_f, win_fs, {0}},
{Mod1Mask, XK_Tab, win_next, {0}},
{Mod1Mask, XK_Tab, win_next, {0}},
{Mod1Mask|ShiftMask, XK_Tab, win_prev, {0}},
{MOD, XK_d, run, {.com = menu}}, {MOD, XK_d, run, {.com = menu}},
{MOD, XK_w, run, {.com = colors}}, {MOD, XK_w, run, {.com = colors}},

View File

@ -0,0 +1,23 @@
diff --git a/sowm.c b/sowm.c
index 0d74d4b..ff70968 100644
--- a/sowm.c
+++ b/sowm.c
@@ -326,7 +326,17 @@ void win_del(Window w) {
"Shoot first and don't ask questions later?.."
*/
void win_kill() {
- if (win_current() != root) XKillClient(d, cur);
+ if (win_current() == root) return;
+
+ XEvent ev = { .type = ClientMessage };
+
+ ev.xclient.window = cur;
+ ev.xclient.format = 32;
+ ev.xclient.message_type = XInternAtom(d, "WM_PROTOCOLS", True);
+ ev.xclient.data.l[0] = XInternAtom(d, "WM_DELETE_WINDOW", True);
+ ev.xclient.data.l[1] = CurrentTime;
+
+ XSendEvent(d, cur, False, NoEventMask, &ev);
}
/*

View File

@ -0,0 +1,120 @@
diff --git a/Makefile b/Makefile
index 2549d3a..04c2222 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
CFLAGS+= -std=c99 -Wall -Wextra -pedantic -Wno-deprecated-declarations
-LDADD+= -lX11
+LDADD+= -lX11 -lXext
LDFLAGS=
PREFIX?= /usr
BINDIR?= $(PREFIX)/bin
diff --git a/config.def.h b/config.def.h
index 864c9a7..1525894 100644
--- a/config.def.h
+++ b/config.def.h
@@ -2,6 +2,7 @@
#define CONFIG_H
#define MOD Mod4Mask
+#define ROUND_CORNERS 20
const char* menu[] = {"dmenu_run", 0};
const char* term[] = {"st", 0};
diff --git a/sowm.c b/sowm.c
index b9e8867..4c0b3fa 100644
--- a/sowm.c
+++ b/sowm.c
@@ -3,6 +3,7 @@
#include <X11/Xlib.h>
#include <X11/XF86keysym.h>
#include <X11/keysym.h>
+#include <X11/extensions/shape.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
@@ -59,6 +59,7 @@ static void win_del(Window w);
static void win_fs();
static void win_kill();
static void win_next();
+static void win_round_corners(Window w, int rad);
static void win_to_ws(const Arg arg);
static void ws_go(const Arg arg);
static void ws_save(int i);
@@ -179,6 +179,9 @@ void notify_motion(XEvent *e) {
attr.y + (mouse.button == 1 ? yd : 0),
attr.width + (mouse.button == 3 ? xd : 0),
attr.height + (mouse.button == 3 ? yd : 0));
+
+ if (mouse.button == 3)
+ win_round_corners(mouse.subwindow, ROUND_CORNERS);
}
/*
@@ -367,9 +367,58 @@ void win_fs() {
} else
XMoveResizeWindow(d, cur, c->a.x, c->a.y, c->a.width, c->a.height);
+
+ win_round_corners(cur, c->f ? 0 : ROUND_CORNERS);
}
}
+/*
+ Round the corners of the desired window.
+
+ This isn't included in the actual source as it
+ requires the 'shape' extension to Xorg and I'd
+ like to keep the original source simple.
+
+ This is very similar to the rounded corners
+ implementations in the 'dwm' and 'openbox'
+ patches.
+*/
+void win_round_corners(Window w, int rad) {
+ XWindowAttributes attr2;
+ XGetWindowAttributes(d, w, &attr2);
+
+ int dia = 2 * rad;
+ int ww = attr2.width;
+ int wh = attr2.height;
+
+ if (ww < dia || wh < dia) return;
+
+ Pixmap mask = XCreatePixmap(d, w, ww, wh, 1);
+
+ if (!mask) return;
+
+ XGCValues xgcv;
+ GC shape_gc = XCreateGC(d, mask, 0, &xgcv);
+
+ if (!shape_gc) {
+ XFreePixmap(d, mask);
+ return;
+ }
+
+ XSetForeground(d, shape_gc, 0);
+ XFillRectangle(d, mask, shape_gc, 0, 0, ww, wh);
+ XSetForeground(d, shape_gc, 1);
+ XFillArc(d, mask, shape_gc, 0, 0, dia, dia, 0, 23040);
+ XFillArc(d, mask, shape_gc, ww-dia-1, 0, dia, dia, 0, 23040);
+ XFillArc(d, mask, shape_gc, 0, wh-dia-1, dia, dia, 0, 23040);
+ XFillArc(d, mask, shape_gc, ww-dia-1, wh-dia-1, dia, dia, 0, 23040);
+ XFillRectangle(d, mask, shape_gc, rad, 0, ww-dia, wh);
+ XFillRectangle(d, mask, shape_gc, 0, rad, ww, wh-dia);
+ XShapeCombineMask(d, w, ShapeBounding, 0, 0, mask, ShapeSet);
+ XFreePixmap(d, mask);
+ XFreeGC(d, shape_gc);
+ }
+
/*
This function simply moves the focused window to
the desired desktop.
@@ -511,6 +511,7 @@ void map_request(XEvent *e) {
win_center((Arg){.i = w});
XMapWindow(d, w);
+ win_round_corners(w, ROUND_CORNERS);
FOC(w);
win_add(w);
}

583
sowm.c
View File

@ -3,180 +3,441 @@
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/XF86keysym.h> #include <X11/XF86keysym.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include "sowm.h" /*
This iterates over the current desktop's
window list and is inserted where needed.
*/
#define WIN (c=list;c;c=c->next)
static client *list = {0}, *ws_list[10] = {0}, *cur; /*
static int ws = 1, sw, sh, wx, wy, numlock = 0; This sets focus to the given window. It
static unsigned int ww, wh; could very well be a function but I feel
it belongs here.
*/
#define FOC(W) XSetInputFocus(d, W, RevertToParent, CurrentTime);
static Display *d; typedef union {
static XButtonEvent mouse; const char** com;
static Window root; 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 client;
struct client{
client *next, *prev;
Window w;
XWindowAttributes a;
int f;
};
typedef struct desktop desktop;
struct desktop{client *list;};
static void button_press(XEvent *e);
static void button_release();
static void configure_request(XEvent *e);
static void key_grab();
static void key_press(XEvent *e);
static void map_request(XEvent *e);
static void notify_destroy(XEvent *e);
static void notify_enter(XEvent *e);
static void notify_motion(XEvent *e);
static void run(const Arg arg);
static void win_add(Window w);
static void win_center(const Arg arg);
static void win_del(Window w);
static void win_fs();
static void win_kill();
static void win_next();
static void win_to_ws(const Arg arg);
static void ws_go(const Arg arg);
static void ws_save(int i);
static void ws_sel(int i);
static client *list = {0};
static desktop ws_list[10];
static int ws = 1, sh, sw, s;
static Display *d;
static Window root, cur;
static XButtonEvent mouse;
static XWindowAttributes attr;
#include "config.h"
/*
The list of events to subscribe to and the paired functions
to call on an event.
*/
static void (*events[LASTEvent])(XEvent *e) = { static void (*events[LASTEvent])(XEvent *e) = {
[ButtonPress] = button_press, [ButtonPress] = button_press,
[ButtonRelease] = button_release, [ButtonRelease] = button_release,
[ConfigureRequest] = configure_request, [ConfigureRequest] = configure_request,
[KeyPress] = key_press, [KeyPress] = key_press,
[MapRequest] = map_request, [MapRequest] = map_request,
[MappingNotify] = mapping_notify,
[DestroyNotify] = notify_destroy, [DestroyNotify] = notify_destroy,
[EnterNotify] = notify_enter, [EnterNotify] = notify_enter,
[MotionNotify] = notify_motion [MotionNotify] = notify_motion
}; };
#include "config.h" /*
'sowm' doesn't keep track of the currently focused window
and instead grabs window under the cursor when needed.
void win_focus(client *c) { This is a super lazy way of handling current focus, however
cur = c; it aligns perfectly with mouse-follows-focus.
XSetInputFocus(d, cur->w, RevertToParent, CurrentTime);
Logic below will select a real window if this function
returns the 'root' window.
This function returns the current window while at the same
time defining a global variable to contain its value. This
allows for stupidily simple usage.
Example: if (win_current() != root) XKillClient(d, cur);
The value can be used as function output and then
the same value can be used as a variable directly afterwards.
*/
Window win_current() {
XGetInputFocus(d, &cur, (int[]){1});
return cur;
} }
/*
When a window is destroyed it is first removed from the
current desktop's window list and finally focus is shifted.
Focus goes to the window under the cursor if it is *not*
the root window. If it is the root window, focus goes to
the first window in the desktop.
*/
void notify_destroy(XEvent *e) { void notify_destroy(XEvent *e) {
win_del(e->xdestroywindow.window); win_del(e->xdestroywindow.window);
if (list) win_focus(list->prev); if (list) FOC(win_current() == root ? list->w : cur);
} }
/*
When the mouse enters or leaves a window this function
handles which window shall be focused next.
The while loop firstly compresses all 'EnterNotify'
events down to only the latest which is an optimization
when focus changes very quickly (e.g a desktop focus).
There's no use in computing each and every event as we
only really care about the newest one.
Focus is only then changed if the mouse has entered a
window which is *not* the root window.
*/
void notify_enter(XEvent *e) { void notify_enter(XEvent *e) {
while(XCheckTypedEvent(d, EnterNotify, e)); while(XCheckTypedEvent(d, EnterNotify, e));
for win if (c->w == e->xcrossing.window) win_focus(c); if (e->xcrossing.window != root) FOC(e->xcrossing.window)
} }
void notify_motion(XEvent *e) { /*
if (!mouse.subwindow || cur->f) return; When the mouse is moved and the paired modifier is
pressed this function handles a window move or a window
resize.
while(XCheckTypedEvent(d, MotionNotify, e)); 'mouse' is defined on a modifier+mouse press and then
discarded on a modifier+mouse release.
The while loop firstly compresses all 'MotionNotify'
events down to only the latest which is an optimization
when motion happens very quickly.
There's no use in computing each and every event as we
only really care about the newest one.
The window is then moved or resized.
*/
void notify_motion(XEvent *e) {
if (mouse.subwindow == None) return;
int xd = e->xbutton.x_root - mouse.x_root; int xd = e->xbutton.x_root - mouse.x_root;
int yd = e->xbutton.y_root - mouse.y_root; int yd = e->xbutton.y_root - mouse.y_root;
while(XCheckTypedEvent(d, MotionNotify, e));
XMoveResizeWindow(d, mouse.subwindow, XMoveResizeWindow(d, mouse.subwindow,
wx + (mouse.button == 1 ? xd : 0), attr.x + (mouse.button == 1 ? xd : 0),
wy + (mouse.button == 1 ? yd : 0), attr.y + (mouse.button == 1 ? yd : 0),
MAX(1, ww + (mouse.button == 3 ? xd : 0)), attr.width + (mouse.button == 3 ? xd : 0),
MAX(1, wh + (mouse.button == 3 ? yd : 0))); attr.height + (mouse.button == 3 ? yd : 0));
} }
void key_press(XEvent *e) { /*
KeySym keysym = XkbKeycodeToKeysym(d, e->xkey.keycode, 0, 0); This function initializes all key bindings defined in 'config.h'.
Simple stuff, nothing fancy or different from other window
managers happens here.
*/
void key_grab() {
KeyCode code;
for (unsigned int i=0; i < sizeof(keys)/sizeof(*keys); ++i) for (unsigned int i=0; i < sizeof(keys)/sizeof(*keys); ++i)
if (keys[i].keysym == keysym && if ((code = XKeysymToKeycode(d, keys[i].keysym)))
mod_clean(keys[i].mod) == mod_clean(e->xkey.state)) XGrabKey(d, code, keys[i].mod, root,
True, GrabModeAsync, GrabModeAsync);
}
/*
This function fires on a key press and checks to see if there
is a matching and defined key binding. If there is a match the
function bound to the key is executed.
The deprecated 'XKeycodeToKeysym' is used as the replacement
requires an additional include and I want to keep them to a
minimum.
I highly doubt this deprecated function goes away any time soon
and worst case, I simply update this code. Win-win.
*/
void key_press(XEvent *e) {
KeySym keysym = XKeycodeToKeysym(d, e->xkey.keycode, 0);
for (unsigned int i=0; i < sizeof(keys)/sizeof(*keys); ++i)
if (keys[i].keysym == keysym && keys[i].mod == e->xkey.state)
keys[i].function(keys[i].arg); keys[i].function(keys[i].arg);
} }
void button_press(XEvent *e) { /*
if (!e->xbutton.subwindow) return; On a mouse button press the window below the cursor's
attributes are stored, the window is raised and the 'mouse'
global is set.
win_size(e->xbutton.subwindow, &wx, &wy, &ww, &wh); Setting the 'mouse' global tells the motion handling function
that it should operate on the window as the user desires a move
or resize.
*/
void button_press(XEvent *e) {
if (e->xbutton.subwindow == None) return;
XGetWindowAttributes(d, e->xbutton.subwindow, &attr);
XRaiseWindow(d, e->xbutton.subwindow); XRaiseWindow(d, e->xbutton.subwindow);
mouse = e->xbutton; mouse = e->xbutton;
} }
void button_release(XEvent *e) { /*
mouse.subwindow = 0; On a mouse button release we simply unset the 'mouse' global
} as all of this mouse pointer nonsense is done.
void win_add(Window w) { We also reset the current window's fullscreen state as it is
no longer at 0,0+[screen_width]X[screen_height].
*/
void button_release() {
client *c; client *c;
if (!(c = (client *) calloc(1, sizeof(client)))) for WIN if (c->w == mouse.subwindow) c->f = 0;
mouse.subwindow = None;
}
/*
This function is called whenever a window is mapped to the
screen or moved to another desktop.
Memory is allocated for the new window and the current
desktop's window list is updated.
*/
void win_add(Window w) {
client *c, *t;
if (!(c = (client *)calloc(1, sizeof(client))))
exit(1); exit(1);
c->w = w; if (!list) {
c->next = c->prev = 0;
if (list) { c->w = w;
list->prev->next = c; list = c;
c->prev = list->prev;
list->prev = c;
c->next = list;
} else { } else {
list = c; for (t=list;t->next;t=t->next);
list->prev = list->next = list;
c->next = 0;
c->prev = t;
c->w = w;
t->next = c;
} }
ws_save(ws); ws_save(ws);
} }
/*
This function is called whenever a window is destoyed
or moved to another desktop.
Memory is freed and the current desktop's window list
is updated.
*/
void win_del(Window w) { void win_del(Window w) {
client *x = 0; client *c;
for win if (c->w == w) x = c; for WIN if (c->w == w) {
if (!c->prev && !c->next) {
free(list);
list = 0;
ws_save(ws);
return;
}
if (!list || !x) return; if (!c->prev) {
if (x->prev == x) list = 0; list = c->next;
if (list == x) list = x->next; c->next->prev = 0;
if (x->next) x->next->prev = x->prev;
if (x->prev) x->prev->next = x->next;
free(x); } else if (!c->next) {
ws_save(ws); c->prev->next = 0;
}
void win_kill(const Arg arg) { } else {
if (cur) XKillClient(d, cur->w); c->prev->next = c->next;
} c->next->prev = c->prev;
}
void win_center(const Arg arg) { free(c);
if (!cur) return; ws_save(ws);
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);
} }
} }
/*
This function is called from a key binding to
close the currently focused window.
This differs from other window managers as we skip
the questions and go straight to the killing of
the window.
When I want to close a window I'm not asking, I
want the window closed and so it should immediately
close.
"Shoot first and don't ask questions later?.."
*/
void win_kill() {
if (win_current() != root) XKillClient(d, cur);
}
/*
This function simply centers the window passed as
an argument. If the argument is '0', use the
currently focused window.
*/
void win_center(const Arg arg) {
Window w = arg.w ? arg.w : win_current();
XGetWindowAttributes(d, w, &attr);
XMoveWindow(d, w, sw / 2 - attr.width / 2,
sh / 2 - attr.height / 2);
}
/*
This function toggles the fullscreen state for the
window passed as an argument.
The window's data stucture holds an integer which
is set to '0' for False and '1' for True.
When a window is set to fullscreen it is simply
resized to fit the screen and the prior size and
positioning is stored so it can be restored when
the window is un-fullscreened.
*/
void win_fs() {
client *c;
win_current();
for WIN if (c->w == cur) {
if ((c->f = c->f == 0 ? 1 : 0)) {
XGetWindowAttributes(d, cur, &c->a);
XMoveResizeWindow(d, cur, 0, 0, sw, sh);
} else
XMoveResizeWindow(d, cur, c->a.x, c->a.y, c->a.width, c->a.height);
}
}
/*
This function simply moves the focused window to
the desired desktop.
It firstly adds the window to the destination
desktop's window list and secondly deletes it
from the current desktop's window list.
The window is then unmapped from the screen and
the focus is shifted to the first window in the
list.
*/
void win_to_ws(const Arg arg) { void win_to_ws(const Arg arg) {
int tmp = ws; int tmp = ws;
win_current();
if (arg.i == tmp) return; if (arg.i == tmp) return;
ws_sel(arg.i); ws_sel(arg.i);
win_add(cur->w); win_add(cur);
ws_save(arg.i); ws_save(arg.i);
ws_sel(tmp); ws_sel(tmp);
win_del(cur->w); win_del(cur);
XUnmapWindow(d, cur->w); XUnmapWindow(d, cur);
ws_save(tmp); ws_save(tmp);
if (list) win_focus(list); if (list) FOC(list->w);
} }
void win_prev(const Arg arg) { /*
if (!cur) return; This function focuses the next window in the
current desktop's window list.
XRaiseWindow(d, cur->prev->w); If the end of the window list is reached it
win_focus(cur->prev); wraps back around to the start of the list.
The newly focused window is then raised to
the top of the stack.
*/
void win_next() {
win_current();
client *c;
if (list) {
for WIN if (c->w == cur) break;
c = c->next ? c->next : list;
FOC(c->w);
XRaiseWindow(d, c->w);
}
} }
void win_next(const Arg arg) { /*
if (!cur) return; This function changes the focus to another desktop.
XRaiseWindow(d, cur->next->w); To make this operation invisible the destination
win_focus(cur->next); desktop's windows are mapped first and the previous
} desktop's windows are then unmapped afterwards.
Finally, focus is shifted to the first window on the
destination desktop's window list.
*/
void ws_go(const Arg arg) { void ws_go(const Arg arg) {
client *c;
int tmp = ws; int tmp = ws;
if (arg.i == ws) return; if (arg.i == ws) return;
@ -184,53 +445,82 @@ void ws_go(const Arg arg) {
ws_save(ws); ws_save(ws);
ws_sel(arg.i); ws_sel(arg.i);
for win XMapWindow(d, c->w); if (list) for WIN XMapWindow(d, c->w);
ws_sel(tmp); ws_sel(tmp);
for win XUnmapWindow(d, c->w); if (list) for WIN XUnmapWindow(d, c->w);
ws_sel(arg.i); ws_sel(arg.i);
if (list) win_focus(list); else cur = 0; if (list) FOC(list->w);
} }
/*
This function saves the current desktop's window list.
Simple, nothing to see here.
*/
void ws_save(int i) {
ws_list[i].list = list;
}
/*
This function restores a saved desktop's window list.
Simple, nothing to see here.
*/
void ws_sel(int i) {
list = ws_list[i].list;
ws = i;
}
/*
This function allows a window to request a size,
position and other attributes.
This is required so programs like Firefox or MPV
are able to display and function correctly.
*/
void configure_request(XEvent *e) { void configure_request(XEvent *e) {
XConfigureRequestEvent *ev = &e->xconfigurerequest; XConfigureRequestEvent *ev = &e->xconfigurerequest;
XWindowChanges wc;
XConfigureWindow(d, ev->window, ev->value_mask, &(XWindowChanges) { wc.x = ev->x;
.x = ev->x, wc.y = ev->y;
.y = ev->y, wc.width = ev->width;
.width = ev->width, wc.height = ev->height;
.height = ev->height, wc.sibling = ev->above;
.sibling = ev->above, wc.stack_mode = ev->detail;
.stack_mode = ev->detail
}); XConfigureWindow(d, ev->window, ev->value_mask, &wc);
} }
/*
This function is executed whenever a window is mapped to
the screen.
The window is centered, mapped to the screen, focused and
finally added to the current desktop's window list.
'XSelectInput' is called to subscribe to various events
related to the window. For example, this is used to get
focus-follows-cursor to work.
*/
void map_request(XEvent *e) { void map_request(XEvent *e) {
Window w = e->xmaprequest.window; Window w = e->xmaprequest.window;
XSelectInput(d, w, StructureNotifyMask|EnterWindowMask); XSelectInput(d, w, PropertyChangeMask|StructureNotifyMask|
win_size(w, &wx, &wy, &ww, &wh); EnterWindowMask|FocusChangeMask);
win_add(w);
cur = list->prev;
if (wx + wy == 0) win_center((Arg){0});
win_center((Arg){.i = w});
XMapWindow(d, w); XMapWindow(d, w);
win_focus(list->prev); FOC(w);
} win_add(w);
void mapping_notify(XEvent *e) {
XMappingEvent *ev = &e->xmapping;
if (ev->request == MappingKeyboard || ev->request == MappingModifier) {
XRefreshKeyboardMapping(ev);
input_grab(root);
}
} }
/*
This function is executed by keybindings to run the
specified program. Simple enough.
*/
void run(const Arg arg) { void run(const Arg arg) {
if (fork()) return; if (fork()) return;
if (d) close(ConnectionNumber(d)); if (d) close(ConnectionNumber(d));
@ -239,51 +529,58 @@ void run(const Arg arg) {
execvp((char*)arg.com[0], (char**)arg.com); execvp((char*)arg.com[0], (char**)arg.com);
} }
void input_grab(Window root) { /*
unsigned int i, j, modifiers[] = {0, LockMask, numlock, numlock|LockMask}; This window manager ignores all Xorg related errors.
XModifierKeymap *modmap = XGetModifierMapping(d);
KeyCode code;
for (i = 0; i < 8; i++) The window manager either crashes (due to Xorg or
for (int k = 0; k < modmap->max_keypermod; k++) itself) or it continues on its merry way.
if (modmap->modifiermap[i * modmap->max_keypermod + k]
== XKeysymToKeycode(d, 0xff7f))
numlock = (1 << i);
XUngrabKey(d, AnyKey, AnyModifier, root); The only errors which are handled are failed memory
allocations or a failure to open the display on start.
*/
int xerror() { return 0; }
for (i = 0; i < sizeof(keys)/sizeof(*keys); i++) /*
if ((code = XKeysymToKeycode(d, keys[i].keysym))) Initialize the window manager by registering all
for (j = 0; j < sizeof(modifiers)/sizeof(*modifiers); j++) keybindings, setting some globals and starting the
XGrabKey(d, code, keys[i].mod | modifiers[j], root, event loop.
True, GrabModeAsync, GrabModeAsync);
for (i = 1; i < 4; i += 2) There's no 'XClosDisplay' or clean up as the only way
for (j = 0; j < sizeof(modifiers)/sizeof(*modifiers); j++) to exit this window manager is to kill the process.
XGrabButton(d, i, MOD | modifiers[j], root, True,
ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
GrabModeAsync, GrabModeAsync, 0, 0);
XFreeModifiermap(modmap);
}
This fires up Xorg's internal clean up which covers
everything allocated and executed here. It's free!
*/
int main(void) { int main(void) {
XEvent ev; XEvent ev;
if (!(d = XOpenDisplay(0))) exit(1); if (!(d = XOpenDisplay(0x0))) return 0;
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
XSetErrorHandler(xerror); XSetErrorHandler(xerror);
int s = DefaultScreen(d); s = DefaultScreen(d);
root = RootWindow(d, s); root = RootWindow(d, s);
sw = XDisplayWidth(d, s); sw = XDisplayWidth(d, s);
sh = XDisplayHeight(d, s); sh = XDisplayHeight(d, s);
key_grab();
ws_go((Arg){.i = 1});
XSelectInput(d, root, SubstructureNotifyMask|
SubstructureRedirectMask|EnterWindowMask|LeaveWindowMask);
XGrabButton(d, 1, Mod4Mask, root, True,
ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(d, 3, Mod4Mask, root, True,
ButtonPressMask|ButtonReleaseMask|PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, None);
XSelectInput(d, root, SubstructureRedirectMask);
XDefineCursor(d, root, XCreateFontCursor(d, 68)); XDefineCursor(d, root, XCreateFontCursor(d, 68));
input_grab(root);
while (1 && !XNextEvent(d, &ev)) // 1 && will forever be here. while(1 && !XNextEvent(d, &ev))
if (events[ev.type]) events[ev.type](&ev); if (events[ev.type]) events[ev.type](&ev);
} }

58
sowm.h
View File

@ -1,58 +0,0 @@
#include <X11/Xlib.h>
#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; }