aboutsummaryrefslogtreecommitdiff
path: root/monsterwm.c
diff options
context:
space:
mode:
authorIvan Kanakarakis <ivan.kanak@gmail.com>2012-01-14 15:07:52 +0200
committerIvan Kanakarakis <ivan.kanak@gmail.com>2012-01-14 15:07:52 +0200
commitfeca569a5d33b6bea6b8350c268b1bdcf69c3756 (patch)
tree880550e647e7c38ff19887ce51cdf9ba2e786df6 /monsterwm.c
parent490b6e6cacba515bb87ba28ef0d17e11fdff26f2 (diff)
basic code documentation
Diffstat (limited to 'monsterwm.c')
-rw-r--r--monsterwm.c195
1 files changed, 188 insertions, 7 deletions
diff --git a/monsterwm.c b/monsterwm.c
index 4e814d4..d2609ab 100644
--- a/monsterwm.c
+++ b/monsterwm.c
@@ -17,17 +17,29 @@
#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
#define BUTTONMASK ButtonPressMask|ButtonReleaseMask
+/* mouse motion actions */
enum { RESIZE, MOVE, };
+/* tiling layout modes */
enum { TILE, MONOCLE, BSTACK, GRID, };
+/* wm and net atoms selected through wmatoms and netatoms arrays */
enum { WM_PROTOCOLS, WM_DELETE_WINDOW, WM_COUNT };
enum { NET_SUPPORTED, NET_FULLSCREEN, NET_WM_STATE, NET_ACTIVE, NET_COUNT };
-/* structs */
+/* argument structure to be passed to function by config.h
+ * com - a command to run
+ * i - an integer to indicate different states
+ */
typedef union {
const char** com;
const int i;
} Arg;
+/* a key struct represents a combination of
+ * mod - a modifier mask
+ * keysym - and the key pressed
+ * func - the function to be triggered because of the above combo
+ * arg - the argument to the function
+ */
typedef struct {
unsigned int mod;
KeySym keysym;
@@ -35,6 +47,12 @@ typedef struct {
const Arg arg;
} key;
+/* a button struct represents a combination of
+ * mask - a modifier mask
+ * button - and the mouse button pressed
+ * func - the function to be triggered because of the above combo
+ * arg - the argument to the function
+ */
typedef struct {
unsigned int mask;
unsigned int button;
@@ -42,12 +60,34 @@ typedef struct {
const Arg arg;
} Button;
+/* a client is a wrapper to a window that additionally
+ * holds some properties for that window
+ *
+ * next - the client after this one, or NULL if the current is the only or last client
+ * isurgent - the window received an urgent hint
+ * istransient - the window is transient
+ * isfullscreen - the window is fullscreen
+ * isfloating - the window is floating
+ * win - the window
+ *
+ * istransient is separate from isfloating as floating window can be reset
+ * to their tiling positions, while the transients will always be floating
+ */
typedef struct client {
struct client *next;
Bool isurgent, istransient, isfullscreen, isfloating;
Window win;
} client;
+/* properties of each desktop
+ * master_size - the size of the master window
+ * mode - the desktop's tiling layout mode
+ * growth - growth factor of the first stack window
+ * head - the start of the client list
+ * current - the currently highlighted window
+ * prevfocus - the client that previously had focus
+ * showpanel - the visibility status of the panel
+ */
typedef struct {
int master_size;
int mode;
@@ -58,6 +98,12 @@ typedef struct {
Bool showpanel;
} desktop;
+/* define behavior of certain applications
+ * configured in config.h
+ * class - the class or name of the instance
+ * desktop - what desktop it should be spawned at
+ * follow - whether to change desktop focus to the specified desktop
+ */
typedef struct {
const char *class;
const int desktop;
@@ -137,7 +183,9 @@ static client *head, *prevfocus, *current;
static Atom wmatoms[WM_COUNT], netatoms[NET_COUNT];
static desktop desktops[DESKTOPS];
-/* events array */
+/* events array
+ * on receival of a new event, call the appropriate function to handle it
+ */
static void (*events[LASTEvent])(XEvent *e) = {
[ButtonPress] = buttonpress,
[ClientMessage] = clientmessage,
@@ -150,6 +198,9 @@ static void (*events[LASTEvent])(XEvent *e) = {
[UnmapNotify] = unmapnotify,
};
+/* create a new client and add the new window
+ * window should notify of property change events
+ */
void addwindow(Window w) {
client *c, *t;
if (!(c = (client *)calloc(1, sizeof(client))))
@@ -168,6 +219,7 @@ void addwindow(Window w) {
XSelectInput(dis, ((current = c)->win = w), PropertyChangeMask|(FOLLOW_MOUSE?EnterWindowMask:0));
}
+/* on the press of a button check to see if there's a binded function to call */
void buttonpress(XEvent *e) {
client *c = wintoclient(e->xbutton.window);
if (!c) return;
@@ -181,6 +233,12 @@ void buttonpress(XEvent *e) {
}
}
+/* focus another desktop
+ * to avoid flickering
+ * first map the new windows
+ * if the layout mode is fullscreen map only one window
+ * then unmap previous windows
+ */
void change_desktop(const Arg *arg) {
if (arg->i == current_desktop) return;
previous_desktop = current_desktop;
@@ -195,6 +253,9 @@ void change_desktop(const Arg *arg) {
desktopinfo();
}
+/* remove all windows in all desktops
+ * get the all windows and send a delete message
+ */
void cleanup(void) {
Window root_return;
Window parent_return;
@@ -209,6 +270,12 @@ void cleanup(void) {
XSetInputFocus(dis, PointerRoot, RevertToPointerRoot, CurrentTime);
}
+/* move a client to another desktop
+ * store the client's window
+ * remove the client
+ * add the window to the new desktop
+ * if defined change focus to the new desktop
+ */
void client_to_desktop(const Arg *arg) {
if (arg->i == current_desktop || !current) return;
Window w = current->win;
@@ -228,10 +295,15 @@ void client_to_desktop(const Arg *arg) {
desktopinfo();
}
-/* check if window requested fullscreen wm_state
- * To change the state of a mapped window, a client MUST send a _NET_WM_STATE client message to the root window
- * message_type must be _NET_WM_STATE, data.l[0] is the action to be taken, data.l[1] is the property to alter
- * three actions: remove/unset _NET_WM_STATE_REMOVE=0, add/set _NET_WM_STATE_ADD=1, toggle _NET_WM_STATE_TOGGLE=2
+/* check if window requested fullscreen or activation
+ * To change the state of a mapped window, a client MUST
+ * send a _NET_WM_STATE client message to the root window
+ * message_type must be _NET_WM_STATE
+ * data.l[0] is the action to be taken
+ * data.l[1] is the property to alter three actions:
+ * remove/unset _NET_WM_STATE_REMOVE=0
+ * add/set _NET_WM_STATE_ADD=1,
+ * toggle _NET_WM_STATE_TOGGLE=2
*/
void clientmessage(XEvent *e) {
client *c = wintoclient(e->xclient.window);
@@ -243,6 +315,11 @@ void clientmessage(XEvent *e) {
update_current(NULL);
}
+/* a configure request means that the window requested changes in its geometry
+ * state. if the window is fullscreen discard and fill the screen, else set the
+ * appropriate values as requested, and tile the window again so that it fills
+ * the gaps that otherwise could have been created
+ */
void configurerequest(XEvent *e) {
client *c = wintoclient(e->xconfigurerequest.window);
if (c && c->isfullscreen)
@@ -262,6 +339,19 @@ void configurerequest(XEvent *e) {
tile();
}
+/* output info about the desktops on standard output stream
+ *
+ * the info is a list of ':' separated values for each desktop
+ * desktop to desktop info is separated by ' ' single spaces
+ * the info values are
+ * the desktop number/id
+ * the desktop's client count
+ * the desktop's tiling layout mode/id
+ * whether the desktop is the current focused (1) or not (0)
+ * whether any client in that desktop has received an urgent hint
+ *
+ * once the info is collected, immediately flush the stream
+ */
void desktopinfo(void) {
Bool urgent = False;
int cd = current_desktop, n=0, d=0;
@@ -273,12 +363,18 @@ void desktopinfo(void) {
select_desktop(cd);
}
+/* a destroy notification is received when a window is being closed
+ * on receival, remove the appropriate client that held that window
+ */
void destroynotify(XEvent *e) {
client *c = wintoclient(e->xdestroywindow.window);
if (c) removeclient(c);
desktopinfo();
}
+/* print a message on standard error stream
+ * and exit with failure exit code
+ */
void die(const char *errstr, ...) {
va_list ap;
va_start(ap, errstr);
@@ -287,6 +383,10 @@ void die(const char *errstr, ...) {
exit(EXIT_FAILURE);
}
+/* when the mouse enters a window's borders
+ * the window, if notifying of such events (EnterWindowMask)
+ * will notify the wm and will get focus
+ */
void enternotify(XEvent *e) {
if (!FOLLOW_MOUSE) return;
client *c = wintoclient(e->xcrossing.window);
@@ -294,10 +394,16 @@ void enternotify(XEvent *e) {
if (e->xcrossing.mode == NotifyNormal && e->xcrossing.detail != NotifyInferior) update_current(c);
}
+/* find and focus the client which received
+ * the urgent hint in the current desktop
+ */
void focusurgent() {
for (client *c=head; c; c=c->next) if (c->isurgent) update_current(c);
}
+/* get a pixel with the requested color
+ * to fill some window area - borders
+ */
unsigned long getcolor(const char* color) {
Colormap map = DefaultColormap(dis, screen);
XColor c;
@@ -307,6 +413,7 @@ unsigned long getcolor(const char* color) {
return c.pixel;
}
+/* set the given client to listen to button events (presses / releases) */
void grabbuttons(client *c) {
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
for (unsigned int b=0; b<LENGTH(buttons); b++)
@@ -315,6 +422,7 @@ void grabbuttons(client *c) {
False, BUTTONMASK, GrabModeAsync, GrabModeAsync, None, None);
}
+/* the wm should listen to key presses */
void grabkeys(void) {
KeyCode code;
XUngrabKey(dis, AnyKey, AnyModifier, root);
@@ -326,6 +434,7 @@ void grabkeys(void) {
XGrabKey(dis, code, keys[k].mod|modifiers[m], root, True, GrabModeAsync, GrabModeAsync);
}
+/* on the press of a key check to see if there's a binded function to call */
void keypress(XEvent *e) {
KeySym keysym;
keysym = XKeycodeToKeysym(dis, (KeyCode)e->xkey.keycode, 0);
@@ -334,16 +443,30 @@ void keypress(XEvent *e) {
keys[i].func(&keys[i].arg);
}
+/* explicitly kill a client - close the highlighted window
+ * send a delete message and remove the client
+ */
void killclient() {
if (!current) return;
sendevent(current->win, WM_DELETE_WINDOW);
removeclient(current);
}
+/* focus the previously focused desktop */
void last_desktop() {
change_desktop(&(Arg){.i = previous_desktop});
}
+/* a map request is received when a window wants to display itself
+ * if the window has override_redirect flag set then it should not be handled
+ * by the wm. if the window already has a client then there is nothing to do.
+ *
+ * get the window class and name instance and try to match against an app rule.
+ * create a client for the window, that client will always be current.
+ * check for transient state, and fullscreen state and the appropriate values.
+ * if the desktop in which the window was spawned is the current desktop then
+ * display the window, else, if set, focus the new desktop.
+ */
void maprequest(XEvent *e) {
static XWindowAttributes wa;
if (XGetWindowAttributes(dis, e->xmaprequest.window, &wa) && wa.override_redirect) return;
@@ -384,6 +507,16 @@ void maprequest(XEvent *e) {
desktopinfo();
}
+/* grab the pointer and get it's current position
+ * all pointer movement events will be reported until it's ungrabbed
+ * until the mouse button has not been released,
+ * grab the interesting events - button press/release and pointer motion
+ * and on on pointer movement resize or move the window under the curson.
+ * if the received event is a map request or a configure request call the
+ * appropriate handler, and stop listening for other events.
+ * Ungrab the poitner and event handling is passed back to run() function.
+ * Once a window has been moved or resized, it's marked as floating.
+ */
void mousemotion(const Arg *arg) {
if (!current) return;
static XWindowAttributes wa;
@@ -498,12 +631,18 @@ void move_up() {
update_current(NULL);
}
+/* cyclic focus the next window
+ * if the window is the last on stack, focus head
+ */
void next_win() {
if (!current || !head->next) return;
update_current((prevfocus = current)->next ? current->next : head);
if (mode == MONOCLE) XMapWindow(dis, current->win);
}
+/* cyclic focus the previous window
+ * if the window is the head, focus the last stack window
+ */
void prev_win() {
if (!current || !head->next) return;
if (head == (prevfocus = current)) while (current->next) current=current->next;
@@ -512,6 +651,9 @@ void prev_win() {
update_current(NULL);
}
+/* property notify is called when one of the window's properties
+ * is changed, such as an urgent hint is received
+ */
void propertynotify(XEvent *e) {
client *c;
if ((c = wintoclient(e->xproperty.window)))
@@ -523,11 +665,19 @@ void propertynotify(XEvent *e) {
}
}
+/* to quit just stop receiving events
+ * run() is stopped and control is back to main()
+ */
void quit(const Arg *arg) {
retval = arg->i;
running = False;
}
+/* remove the specified client
+ * the previous client must point to the next client of the given
+ * the removing client can be on any desktop, so we must return
+ * back the current focused desktop
+ */
void removeclient(client *c) {
client **p = NULL;
int nd = 0, cd = current_desktop;
@@ -542,6 +692,9 @@ void removeclient(client *c) {
free(c);
}
+/* resize the master window - check for boundary size limits
+ * the size of a window can't be less than MINWSZ
+ */
void resize_master(const Arg *arg) {
int msz = master_size + arg->i;
if ((mode == BSTACK ? wh : ww) - msz <= MINWSZ || msz <= MINWSZ) return;
@@ -549,20 +702,24 @@ void resize_master(const Arg *arg) {
tile();
}
+/* resize the first stack window - no boundary checks */
void resize_stack(const Arg *arg) {
growth += arg->i;
tile();
}
+/* jump and focus the 'current + n' desktop */
void rotate_desktop(const Arg *arg) {
change_desktop(&(Arg){.i = (current_desktop + DESKTOPS + arg->i) % DESKTOPS});
}
+/* main event loop - on receival of an event call the appropriate event handler */
void run(void) {
XEvent ev;
while(running && !XNextEvent(dis, &ev)) if (events[ev.type]) events[ev.type](&ev);
}
+/* save specified desktop's properties */
void save_desktop(int i) {
if (i >= DESKTOPS) return;
desktops[i].master_size = master_size;
@@ -574,6 +731,7 @@ void save_desktop(int i) {
desktops[i].prevfocus = prevfocus;
}
+/* set the specified desktop's properties */
void select_desktop(int i) {
if (i >= DESKTOPS || i == current_desktop) return;
save_desktop(current_desktop);
@@ -587,6 +745,7 @@ void select_desktop(int i) {
current_desktop = i;
}
+/* send the given event - WM_DELETE_WINDOW for now */
void sendevent(Window w, int atom) {
if (atom >= WM_COUNT) return;
XEvent ev;
@@ -599,12 +758,18 @@ void sendevent(Window w, int atom) {
XSendEvent(dis, w, False, NoEventMask, &ev);
}
+/* set or unset fullscreen state of client */
void setfullscreen(client *c, Bool fullscreen) {
XChangeProperty(dis, c->win, netatoms[NET_WM_STATE], XA_ATOM, 32, PropModeReplace, (unsigned char*)
((c->isfullscreen = fullscreen) ? &netatoms[NET_FULLSCREEN] : 0), fullscreen);
if (c->isfullscreen) XMoveResizeWindow(dis, c->win, 0, 0, ww + BORDER_WIDTH, wh + BORDER_WIDTH + PANEL_HEIGHT);
}
+/* set initial values
+ * root window - screen height/width - atoms - xerror handler
+ * set masks for reporting events handled by the wm
+ * and propagate the suported net atoms
+ */
void setup(void) {
sigchld();
@@ -653,6 +818,7 @@ void sigchld() {
while(0 < waitpid(-1, NULL, WNOHANG));
}
+/* execute a command */
void spawn(const Arg *arg) {
if (fork() == 0) {
if (dis) close(ConnectionNumber(dis));
@@ -664,17 +830,22 @@ void spawn(const Arg *arg) {
}
}
+/* swap master window with current or
+ * if current is master with the next
+ * if current is not head, then head is
+ * behind us, so move_up until is head
+ */
void swap_master() {
if (!current || !head->next || mode == MONOCLE) return;
for (client *t=head; t; t=t->next) if (t->isfullscreen) return;
/* if current is head swap with next window */
if (current == head) move_down();
- /* if not head, then head is always behind us, so move_up until is head */
else while (current != head) move_up();
update_current(head);
tile();
}
+/* switch the tiling mode and reset all floating windows */
void switch_mode(const Arg *arg) {
if (mode == MONOCLE) for (client *c=head; c; c=c->next) XMapWindow(dis, c->win);
for (client *c=head; c; c=c->next) c->isfloating = False;
@@ -685,6 +856,7 @@ void switch_mode(const Arg *arg) {
desktopinfo();
}
+/* tile all windows of current desktop to the set tiling mode */
void tile(void) {
if (!head) return; /* nothing to arange */
@@ -736,17 +908,25 @@ void tile(void) {
free(c);
}
+/* toggle visibility state of the panel */
void togglepanel() {
showpanel = !showpanel;
tile();
}
+/* windows that request to unmap should lose their
+ * client, so no invisible windows exist on screen
+ */
void unmapnotify(XEvent *e) {
client *c = wintoclient(e->xunmap.window);
if (c && e->xunmap.send_event) removeclient(c);
desktopinfo();
}
+/* update client - set highlighted borders and active window
+ * if no client is given update current
+ * if current is NULL then delete the active window property
+ */
void update_current(client *c) {
if (!current && !c) {
XDeleteProperty(dis, root, netatoms[NET_ACTIVE]);
@@ -771,6 +951,7 @@ void update_current(client *c) {
XSync(dis, False);
}
+/* find to which client the given window belongs to */
client* wintoclient(Window w) {
client *c = NULL;
int d = 0, cd = current_desktop;