├── .gitignore ├── LICENSE ├── Makefile ├── Makefile.freebsd ├── PKGBUILD ├── PKGBUILD-git ├── README.md ├── box.c ├── cli.c ├── client.c ├── ewmh.c ├── goomwwm.1 ├── goomwwm.c ├── goomwwm.desktop ├── goomwwm.h ├── goomwwm.md ├── grab.c ├── handle.c ├── menu.c ├── monitor.c ├── proto.h ├── rule.c ├── screenshot-thumb.jpg ├── screenshot.jpg ├── tag.c ├── textbox.c ├── util.c ├── version.h ├── window.c ├── winlist.c └── wm.c /.gitignore: -------------------------------------------------------------------------------- 1 | goomwwm 2 | goomwwm-debug 3 | roll 4 | release 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X11 License 2 | Copyright (c) 2012 Sean Pringle 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS?=-Wall -Os 2 | LDADD?=`pkg-config --cflags --libs x11 xinerama xft` 3 | 4 | normal: 5 | $(CC) -o goomwwm goomwwm.c $(CFLAGS) $(LDADD) $(LDFLAGS) 6 | 7 | debug: 8 | $(CC) -o goomwwm-debug goomwwm.c $(CFLAGS) -g -DDEBUG $(LDADD) 9 | 10 | proto: 11 | cat *.c | egrep '^(void|int|char|unsigned|client|Window|winlist|box|textbox|XWindow)' | sed -r 's/\)/);/' > proto.h 12 | 13 | docs: 14 | pandoc -s -w man goomwwm.md -o goomwwm.1 15 | 16 | all: proto normal debug docs 17 | 18 | clean: 19 | rm -f goomwwm goomwwm-debug -------------------------------------------------------------------------------- /Makefile.freebsd: -------------------------------------------------------------------------------- 1 | CFLAGS+=-Wall -O2 -I/usr/local/include -I/usr/local/include/freetype2 2 | LDADD+=-lutil -L/usr/local/lib -lX11 -lXft -lXinerama -lXrender -lfontconfig -lfreetype 3 | 4 | normal: 5 | $(CC) -o goomwwm goomwwm.c $(CFLAGS) $(LDADD) $(LDFLAGS) 6 | 7 | debug: 8 | $(CC) -o goomwwm-debug goomwwm.c $(CFLAGS) -g -DDEBUG $(LDADD) 9 | 10 | proto: 11 | cat *.c | egrep '^(void|int|char|unsigned|client|Window|winlist|XWindow)' | sed -r 's/\)/);/' > proto.h 12 | 13 | docs: 14 | pandoc -s -w man goomwwm.md -o goomwwm.1 15 | 16 | all: proto normal debug docs 17 | 18 | clean: 19 | rm -f goomwwm goomwwm-debug 20 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Contributor: Sean Pringle 2 | 3 | pkgname=goomwwm 4 | pkgver=_VER_ 5 | pkgrel=1 6 | pkgdesc="Get out of my way, Window Manager!" 7 | arch=('i686' 'x86_64') 8 | url="http://aerosuidae.net/goomwwm" 9 | license=('MIT') 10 | depends=('libx11' 'libxft' 'freetype2') 11 | optdepends=('dmenu') 12 | makedepends=() 13 | provides=('goomwwm') 14 | conflicts=('goomwwm-git') 15 | source=("http://aerosuidae.net/goomwwm/$pkgname-$pkgver.tar.gz") 16 | md5sums=('_MD5_') 17 | 18 | build() { 19 | cd "${srcdir}/$pkgname-$pkgver" 20 | make 21 | } 22 | 23 | package() { 24 | cd "${srcdir}/$pkgname-$pkgver" 25 | install -Dm 755 $pkgname "$pkgdir/usr/bin/$pkgname" 26 | install -Dm 644 "$pkgname.desktop" "$pkgdir/usr/share/xsessions/$pkgname.desktop" 27 | gzip -c "$pkgname.1" > "$pkgname.1.gz" 28 | install -Dm644 "$pkgname.1.gz" "$pkgdir/usr/share/man/man1/$pkgname.1.gz" 29 | } 30 | -------------------------------------------------------------------------------- /PKGBUILD-git: -------------------------------------------------------------------------------- 1 | # Contributor: Sean Pringle 2 | 3 | pkgname=goomwwm-git 4 | pkgver=20120625 5 | pkgrel=1 6 | pkgdesc="Get out of my way, Window Manager!" 7 | arch=('i686' 'x86_64') 8 | url="http://github.com/seanpringle/goomwwm" 9 | license=('MIT') 10 | depends=('libx11' 'libxft' 'freetype2') 11 | makedepends=('git') 12 | provides=('goomwwm') 13 | conflicts=('goomwwm') 14 | 15 | _gitroot="git://github.com/seanpringle/goomwwm.git" 16 | _gitname="goomwwm" 17 | 18 | build() { 19 | cd "$srcdir" 20 | msg "Connecting to GIT server...." 21 | 22 | if [ -d $_gitname ] ; then 23 | cd $_gitname && git pull origin 24 | msg "The local files are updated." 25 | else 26 | git clone $_gitroot --depth=1 27 | fi 28 | 29 | msg "GIT checkout done or server timeout" 30 | msg "Starting make..." 31 | 32 | rm -rf "$srcdir/$_gitname-build" 33 | cp -r "$srcdir/$_gitname" "$srcdir/$_gitname-build" 34 | cd "$srcdir/$_gitname-build" 35 | 36 | make 37 | } 38 | 39 | package() { 40 | cd "$srcdir/$_gitname-build" 41 | install -Dm 755 $_gitname "$pkgdir/usr/bin/$_gitname" 42 | install -Dm 644 "$_gitname.desktop" "$pkgdir/usr/share/xsessions/$_gitname.desktop" 43 | gzip -c "$_gitname.1" > "$_gitname.1.gz" 44 | install -Dm644 "$_gitname.1.gz" "$pkgdir/usr/share/man/man1/$_gitname.1.gz" 45 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | goomwwm 2 | ======= 3 | 4 | Get out of my way, Window Manager! 5 | 6 | goomwwm is an X11 window manager implemented in C as a cleanroom software project. It manages windows in a minimal floating layout, while providing flexible keyboard-driven controls for window switching, sizing, moving, tagging, and tiling. It is also fast, lightweight, modeless, Xinerama-aware, and EWMH compatible wherever possible. 7 | 8 | Keyboard window movement occurs on a 3x3 fullscreen grid. For example, a top-left aligned window moved to the right would reappear top-center, and if moved again, top-right. 9 | 10 | Keyboard window sizing moves windows through four basic sizes that tile well: 1/9th of screen (1/3 width, 1/3 height), 1/4th, 4/9th, and fullscreen. Combined with EWMH horizontal and vertical maxmimization, plus some snap-to-edge and expand-to-fill-space controls, manual tiling is easy. 11 | 12 | Windows are grouped by tags. Activating a tag raises all windows in the tag. A window may have multiple tags. EWMH panels, pagers, and taskbars see tags as desktops. 13 | 14 | For more detail, see **[this tutorial](http://aerosuidae.net/goomwwm/tutorial)** and the included **man page**. 15 | 16 | ![Alt text](http://aerosuidae.net/goomwwm/goomwwm-1.jpg) 17 | 18 | ![Alt text](http://aerosuidae.net/goomwwm/goomwwm-2.jpg) 19 | 20 | ![Alt text](http://aerosuidae.net/goomwwm/goomwwm-3.jpg) 21 | 22 | ![Alt text](http://aerosuidae.net/goomwwm/goomwwm-4.jpg) -------------------------------------------------------------------------------- /box.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // Xft text box, optionally editable 28 | box* box_create(Window parent, bitmap flags, short x, short y, short w, short h, char *color) 29 | { 30 | box *b = allocate_clear(sizeof(box)); 31 | 32 | b->flags = flags; 33 | b->parent = parent; 34 | box_color(b, color); 35 | 36 | b->window = XCreateSimpleWindow(display, b->parent, 0, 0, 1, 1, 0, None, b->color); 37 | 38 | if (b->flags & BOX_OVERRIDE) 39 | { 40 | XSetWindowAttributes attr; attr.override_redirect = True; 41 | XChangeWindowAttributes(display, b->window, CWOverrideRedirect, &attr); 42 | } 43 | 44 | box_moveresize(b, x, y, w, h); 45 | 46 | return b; 47 | } 48 | 49 | void box_color(box *b, char *color) 50 | { 51 | b->color = color_get(color); 52 | } 53 | 54 | void box_moveresize(box *b, short x, short y, short w, short h) 55 | { 56 | b->x = x; b->y = y; b->w = MAX(1, w); b->h = MAX(1, h); 57 | XMoveResizeWindow(display, b->window, b->x, b->y, b->w, b->h); 58 | } 59 | 60 | void box_show(box *b) 61 | { 62 | XMapWindow(display, b->window); 63 | } 64 | 65 | void box_hide(box *b) 66 | { 67 | XUnmapWindow(display, b->window); 68 | } 69 | 70 | void box_draw(box *b) 71 | { 72 | XSetWindowAttributes attr; attr.background_pixel = b->color; 73 | XChangeWindowAttributes(display, b->window, CWBackPixel, &attr); 74 | XClearWindow(display, b->window); 75 | } 76 | 77 | void box_free(box *b) 78 | { 79 | XDestroyWindow(display, b->window); 80 | free(b); 81 | } 82 | -------------------------------------------------------------------------------- /cli.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | void cli_message(Atom atom, char *cmd) 28 | { 29 | Window cli = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, None, None); 30 | if (cmd) window_set_text_prop(cli, gatoms[GOOMWWM_MESSAGE], cmd); 31 | window_send_message(root, cli, atom, 0, SubstructureNotifyMask | SubstructureRedirectMask); 32 | } 33 | 34 | // command line interface 35 | int cli_main(int argc, char *argv[]) 36 | { 37 | char *arg; 38 | 39 | if ((arg = find_arg_str(argc, argv, "-log", NULL))) 40 | cli_message(gatoms[GOOMWWM_LOG], arg); 41 | 42 | if (find_arg(argc, argv, "-restart") >= 0) 43 | cli_message(gatoms[GOOMWWM_RESTART], argv[0]); 44 | 45 | if ((arg = find_arg_str(argc, argv, "-exec", NULL))) 46 | cli_message(gatoms[GOOMWWM_RESTART], arg); 47 | 48 | if ((arg = find_arg_str(argc, argv, "-ruleset", NULL))) 49 | cli_message(gatoms[GOOMWWM_RULESET], arg); 50 | 51 | if ((arg = find_arg_str(argc, argv, "-rule", NULL))) 52 | cli_message(gatoms[GOOMWWM_RULE], arg); 53 | 54 | if ((arg = find_arg_str(argc, argv, "-findstart", NULL))) 55 | cli_message(gatoms[GOOMWWM_FIND_OR_START], arg); 56 | 57 | if (find_arg(argc, argv, "-quit") >= 0) 58 | cli_message(gatoms[GOOMWWM_QUIT], NULL); 59 | 60 | if ((arg = find_arg_str(argc, argv, "-notice", NULL))) 61 | { 62 | // optional duration in seconds. 0 means SAYMS 63 | int delay = find_arg_int(argc, argv, "-duration", 0); 64 | // bit of a hack for v1... write delay inline 65 | char *tmp = alloca(strlen(arg) + 10); 66 | sprintf(tmp, "%d %s", delay, arg); 67 | cli_message(gatoms[GOOMWWM_NOTICE], tmp); 68 | } 69 | 70 | //TODO: make this a two-way event exchange 71 | usleep(500000); // 0.5s 72 | return EXIT_SUCCESS; 73 | } -------------------------------------------------------------------------------- /ewmh.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // update _NET_CLIENT_LIST 28 | void ewmh_client_list() 29 | { 30 | XSync(display, False); 31 | // this often happens after we've made changes. refresh 32 | reset_cache_inplay(); 33 | 34 | winlist *relevant = winlist_new(); 35 | winlist *mapped = winlist_new(); 36 | int i; Window w; client *c; 37 | 38 | // windows_in_play() returns the stacking order. windows_activated *MAY NOT* have the same order 39 | managed_ascend(i, w, c) if (!client_has_state(c, netatoms[_NET_WM_STATE_SKIP_TASKBAR])) winlist_append(relevant, w, NULL); 40 | XChangeProperty(display, root, netatoms[_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32, PropModeReplace, (unsigned char*)relevant->array, relevant->len); 41 | 42 | // 'windows' list has mapping order of everything. build 'mapped' from 'relevant', ordered by 'windows' 43 | winlist_ascend(windows, i, w) if (winlist_forget(relevant, w)) winlist_append(mapped, w, NULL); 44 | XChangeProperty(display, root, netatoms[_NET_CLIENT_LIST], XA_WINDOW, 32, PropModeReplace, (unsigned char*)mapped->array, mapped->len); 45 | 46 | winlist_free(mapped); 47 | winlist_free(relevant); 48 | } 49 | 50 | // update _NET_ACTIVE_WINDOW 51 | void ewmh_active_window(Window w) 52 | { 53 | XChangeProperty(display, root, netatoms[_NET_ACTIVE_WINDOW], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&w, 1); 54 | } 55 | 56 | // _NET_DESKTOP stuff, taking _NET_WM_STRUT* into account 57 | void ewmh_desktop_list() 58 | { 59 | int i; XWindowAttributes *attr = window_get_attributes(root); 60 | // nine desktops. want more space? buy more monitors and use xinerama :) 61 | unsigned long desktops = TAGS, area[4*TAGS], geo[2], view[2], desktop; 62 | 63 | // this will return the full X screen, not Xinerama screen 64 | workarea mon; monitor_dimensions_struts(-1, -1, &mon); 65 | 66 | // figure out the workarea, less struts 67 | for (i = 0; i < TAGS; i++) 68 | { 69 | area[(i*4)+0] = mon.x; area[(i*4)+1] = mon.y; 70 | area[(i*4)+2] = mon.w; area[(i*4)+3] = mon.h; 71 | } 72 | view[0] = 0; view[1] = 0; 73 | geo[0] = attr->width; //DisplayWidth(display, XScreenNumberOfScreen(attr->screen)); 74 | geo[1] = attr->height; //DisplayHeight(display, XScreenNumberOfScreen(attr->screen)); 75 | desktop = tag_to_desktop(current_tag); 76 | 77 | window_set_cardinal_prop(root, netatoms[_NET_NUMBER_OF_DESKTOPS], &desktops, 1); 78 | window_set_cardinal_prop(root, netatoms[_NET_DESKTOP_GEOMETRY], geo, 2); 79 | window_set_cardinal_prop(root, netatoms[_NET_DESKTOP_VIEWPORT], view, 2); 80 | window_set_cardinal_prop(root, netatoms[_NET_WORKAREA], area, TAGS*4); 81 | window_set_cardinal_prop(root, netatoms[_NET_CURRENT_DESKTOP], &desktop, 1); 82 | } 83 | -------------------------------------------------------------------------------- /goomwwm.1: -------------------------------------------------------------------------------- 1 | .TH GOOMWWM 1 "" 2 | .SH NAME 3 | .PP 4 | goomwwm - Get out of my way, Window Manager! 5 | .SH SYNOPSIS 6 | .PP 7 | \f[B]goomwwm\f[] [ options ] 8 | .SH DESCRIPTION 9 | .PP 10 | \f[B]goomwwm\f[] is an X11 window manager implemented in C as a 11 | cleanroom software project. 12 | It manages windows in a minimal floating layout with normal mouse 13 | controls, while also providing flexible keyboard-driven controls for 14 | window switching, sizing, moving, tagging, and tiling. 15 | It is fast, lightweight, modeless, Xinerama-aware, and EWMH compatible 16 | wherever possible. 17 | .PP 18 | Keyboard window movement occurs on a 3x3 fullscreen grid. 19 | For example, a top-left aligned window moved to the right would reappear 20 | top-center, and if moved again, top-right. 21 | .PP 22 | Keyboard window sizing moves windows through four basic sizes that tile 23 | well: 1/9th of screen (1/3 width, 1/3 height), 1/4th, 4/9th, and 24 | fullscreen. 25 | Combined with EWMH horizontal and vertical maxmimization, plus some 26 | snap-to-edge and expand-to-fill-space controls, manual tiling is easy. 27 | .PP 28 | Windows are grouped by tags. 29 | Activating a tag raises all windows in the tag. 30 | A window may have multiple tags. 31 | EWMH panels, pagers, and taskbars see tags as desktops. 32 | .PP 33 | License: MIT/X11 34 | .SH USAGE 35 | .PP 36 | See options below for custom key combinations. 37 | These are the defaults. 38 | .PP 39 | Below, \f[B]Mod\f[] refers to the global modifier key which is 40 | \f[B]Mod4\f[] by default. 41 | Usually Mod4 is mapped to the left Windows key. 42 | To change it, see \f[B]-modkey\f[]. 43 | .TP 44 | .B Mod-Button1 45 | (Mouse!) Move a window. 46 | .RS 47 | .RE 48 | .TP 49 | .B Mod-Button3 50 | (Mouse!) Resize a window. 51 | .RS 52 | .RE 53 | .TP 54 | .B Mod-[F1-F9] 55 | Set the current tag and raise all its windows. 56 | A window may be in multiple tags. 57 | For EWMH panel and pager integration tags roughly simulate desktops 58 | (always 9), but there will be differences in behavior. 59 | .RS 60 | .RE 61 | .TP 62 | .B Mod-Shift-[F1-F9] 63 | Toggle active window\[aq]s tags without switching current tag. 64 | .RS 65 | .RE 66 | .TP 67 | .B Mod-[1-9] 68 | Do a case-insensitive keyword search for window by WM_CLASS and title. 69 | If found, raise and focus. 70 | If not found, try to execute the keyword and start the app. 71 | See \f[B]-1\f[] through \f[B]-9\f[] args. 72 | .RS 73 | .RE 74 | .TP 75 | .B Mod-Shift-[1-9] 76 | Force execution of keyword used in \f[B]Mod-[1-9]\f[] even if a copy of 77 | the window already exists. 78 | .RS 79 | .RE 80 | .TP 81 | .B Mod-Tab 82 | Switch between all windows by popup menu. 83 | This is partly based on \f[B]dmenu\f[] but more tightly integrated with 84 | the window manager and centered on screen. 85 | Type text to filter the menu entries. 86 | Navigate with the arrow keys. 87 | Return to select. 88 | .RS 89 | .RE 90 | .TP 91 | .B Mod-` (Grave/Backtick) 92 | Switch between all windows in current tag by popup menu. 93 | .RS 94 | .RE 95 | .TP 96 | .B Mod-c 97 | Cycle through windows in the same tag and position as the active window. 98 | .RS 99 | .RE 100 | .TP 101 | .B Mod-Escape 102 | Close the active window. 103 | .RS 104 | .RE 105 | .TP 106 | .B Mod-PageUp 107 | Grow the active window through four basic sizes that tile well together: 108 | 1/9th, 1/4th, 4/9th, or fullscreen. 109 | .RS 110 | .RE 111 | .TP 112 | .B Mod-PageDown 113 | Shrink the active window through the same four sizes. 114 | .RS 115 | .RE 116 | .TP 117 | .B Mod-Shift-PageUp 118 | Increase size of the active window. 119 | .RS 120 | .RE 121 | .TP 122 | .B Mod-Shift-PageDown 123 | Decrease size of the active window. 124 | .RS 125 | .RE 126 | .TP 127 | .B Mod-h 128 | Horizontally tile the active window and others with the same tag, 129 | position, and size. 130 | .RS 131 | .RE 132 | .TP 133 | .B Mod-v 134 | Vertically tile the active window and others with the same tag, 135 | position, and size. 136 | .RS 137 | .RE 138 | .TP 139 | .B Mod-Shift-h 140 | Opposite of Mod-h. 141 | Horizontally un-tile. 142 | .RS 143 | .RE 144 | .TP 145 | .B Mod-Shift-v 146 | Opposite of Mod-v. 147 | Vertically un-tile. 148 | .RS 149 | .RE 150 | .TP 151 | .B Mod-x 152 | Run the launcher (by default: dmenu_run). 153 | .RS 154 | .RE 155 | .TP 156 | .B Mod-a 157 | Toggle _NET_WM_STATE_ABOVE for the active window. 158 | Corners will flash to acknowledge. 159 | .RS 160 | .RE 161 | .TP 162 | .B Mod-b 163 | Toggle _NET_WM_STATE_BELOW for the active window. 164 | Corners will flash to acknowledge. 165 | .RS 166 | .RE 167 | .TP 168 | .B Mod-f 169 | Toggle _NET_WM_STATE_FULLSCREEN for the active window. 170 | Corners will flash to acknowledge. 171 | .RS 172 | .RE 173 | .TP 174 | .B Mod-d 175 | Resize active window to match the window immediately underneath, in the 176 | same tag. 177 | .RS 178 | .RE 179 | .TP 180 | .B Mod-Home 181 | Toggle _NET_WM_STATE_MAXIMIXED_HORZ for the active window. 182 | Corners will flash to acknowledge. 183 | .RS 184 | .RE 185 | .TP 186 | .B Mod-End 187 | Toggle _NET_WM_STATE_MAXIMIXED_VERT for the active window. 188 | Corners will flash to acknowledge. 189 | .RS 190 | .RE 191 | .TP 192 | .B Mod-Return 193 | Expand active window to fill surrounding space without obscuring any 194 | fully visible window. 195 | .RS 196 | .RE 197 | .TP 198 | .B Mod-Backspace 199 | Contract active window to fill an underlying space without obscuring 200 | window that would then be fully visible. 201 | .RS 202 | .RE 203 | .TP 204 | .B Mod-Insert 205 | Toggle vertical move/resize lock for the active window. 206 | .RS 207 | .RE 208 | .TP 209 | .B Mod-Delete 210 | Toggle horizontal move/resize lock for the active window. 211 | .RS 212 | .RE 213 | .TP 214 | .B Mod-Left 215 | Move the active window left within a 3x3 grid. 216 | .RS 217 | .RE 218 | .TP 219 | .B Mod-Right 220 | Move the active window right within a 3x3 grid. 221 | .RS 222 | .RE 223 | .TP 224 | .B Mod-Up 225 | Move the active window up within a 3x3 grid. 226 | .RS 227 | .RE 228 | .TP 229 | .B Mod-Down 230 | Move the active window down within a 3x3 grid. 231 | .RS 232 | .RE 233 | .TP 234 | .B Mod-Shift-Left (Left/Right/Up/Down) 235 | Snap the active window to the nearest border, by direction. 236 | .RS 237 | .RE 238 | .TP 239 | .B Mod-u 240 | Undo the last size/position change for the active window. 241 | Undo is 10 levels deep. 242 | .RS 243 | .RE 244 | .TP 245 | .B Mod-i 246 | Switch focus upward from the active window. 247 | .RS 248 | .RE 249 | .TP 250 | .B Mod-j 251 | Switch focus to the left of the active window. 252 | .RS 253 | .RE 254 | .TP 255 | .B Mod-k 256 | Switch focus downward from the active window. 257 | .RS 258 | .RE 259 | .TP 260 | .B Mod-l 261 | Switch focus to the right of the active window. 262 | .RS 263 | .RE 264 | .TP 265 | .B Mod-Shift-i (i/j/k/l) 266 | Swap the active window position with another window by direction. 267 | .RS 268 | .RE 269 | .TP 270 | .B Mod-t 271 | Toggle the active window\[aq]s membership of the current tag. 272 | .RS 273 | .RE 274 | .TP 275 | .B Mod-m 276 | Cycle tag forward. 277 | .RS 278 | .RE 279 | .TP 280 | .B Mod-n 281 | Cycle tag backward. 282 | .RS 283 | .RE 284 | .TP 285 | .B Mod-w 286 | Display active window title. 287 | .RS 288 | .RE 289 | .TP 290 | .B Mod-, (comma) 291 | Reapply active window rule. 292 | .RS 293 | .RE 294 | .TP 295 | .B Mod-. (period) 296 | Switch between defined rule sets. 297 | .RS 298 | .RE 299 | .TP 300 | .B Mod-/ (slash) 301 | Minimize a window. 302 | .RS 303 | .RE 304 | .TP 305 | .B Mod-[ (left square brancket) 306 | Move and resize a window to cover the left 2/3 of a monitor. 307 | .RS 308 | .RE 309 | .TP 310 | .B Mod-] (right square brancket) 311 | Move and resize a window to cover the right 2/3 of a monitor. 312 | .RS 313 | .RE 314 | .TP 315 | .B Mod-o 316 | Show only windows in the current tag. 317 | Hide everything else. 318 | .RS 319 | .RE 320 | .TP 321 | .B Mod-Pause (press twice) 322 | Quit goomwwm. 323 | .RS 324 | .RE 325 | .SH OPTIONS 326 | .PP 327 | All key combinations use the same global modifier key by default, which 328 | is \f[B]Mod4\f[] (usually Win/Meta). 329 | If the default modifier is changed with \f[B]-modkey\f[] then all key 330 | combinations that do not specify their own custom modifiers will change 331 | to use the new modifier automatically. 332 | .PP 333 | All options below that set a custom key therefore implicitly combine it 334 | with the default modifier key. 335 | For example, the following both mean \f[B]Mod4-a\f[]: 336 | .IP 337 | .nf 338 | \f[C] 339 | goomwwm\ -above\ a 340 | goomwwm\ -above\ mod4-a 341 | \f[] 342 | .fi 343 | .PP 344 | Any combiation of \f[B]shift\f[], \f[B]control\f[], \f[B]mod1\f[] 345 | (usually Alt), \f[B]mod2\f[], \f[B]mod3\f[], \f[B]mod4\f[] (usually 346 | Win/Meta), \f[B]mod5\f[] (sometimes AltGr) may be supplied for any key 347 | combination: 348 | .IP 349 | .nf 350 | \f[C] 351 | goomwwm\ -above\ control-shift-a 352 | \f[] 353 | .fi 354 | .PP 355 | To explicitly bind a key without any modifier, not even the default, use 356 | \f[B]nomod\f[]: 357 | .IP 358 | .nf 359 | \f[C] 360 | goomwwm\ -above\ nomod-f12 361 | \f[] 362 | .fi 363 | .PP 364 | Note that this would capture F12 globally, making it unusable for 365 | anything else. 366 | Use \f[I]nomod\f[] with care. 367 | .TP 368 | .B -1 -2 -3 -4 -5 -6 -7 -8 -9 369 | Set a number key to a keyword to search for a window by WM_CLASS, 370 | application name, or title, and then raise and focus it. 371 | If a window is not found, the string supplied will be executed as a 372 | shell command to start the application. 373 | .RS 374 | .PP 375 | goomwwm -1 chromium -2 firefox -3 xterm 376 | .PP 377 | Above, Mod-1 would match the top-most Chromium window. 378 | .PP 379 | Many applications politely set their WM_CLASS to a sane value (eg, 380 | Chromium uses Chromium, xterm uses XTerm) or append their name to their 381 | window titles, which nicely matches their binary names if we use 382 | case-insensitive string comparison. 383 | This allows us to use the one string to both search and start. 384 | .PP 385 | Applications that are not so friendly can sometimes be wrapped in a 386 | shell script of the appropriate name in your $PATH somewhere. 387 | .PP 388 | Sometimes it can be useful to limit the match to WM_CLASS or name (eg, 389 | when a browser window title includes another app\[aq]s name causing a 390 | false positive). 391 | Simply use \f[B]class:\f[] or \f[B]name:\f[] prefixes: 392 | .PP 393 | goomwwm -1 class:chromium 394 | .RE 395 | .TP 396 | .B -above 397 | Set an X11 key name to toggle _NET_WM_STATE_ABOVE for the active window 398 | (default: XK_a). 399 | .RS 400 | .PP 401 | goomwwm -above a 402 | .RE 403 | .TP 404 | .B -appkeys 405 | Specify the keys to use for app search-or-launch as set by \f[B]-1\f[] 406 | through \f[B]-9\f[] (default: numbers). 407 | .RS 408 | .PP 409 | Valid settings are: 410 | .TP 411 | .B numbers 412 | Use the number keys 1-9 413 | .RS 414 | .RE 415 | .TP 416 | .B functions 417 | Use the function keys F1-F9 418 | .RS 419 | .RE 420 | .PP 421 | If function keys are used for app launchers the number keys will be used 422 | to swap tags, and vice versa. 423 | .RE 424 | .TP 425 | .B -attention 426 | Set the border color (X11 named color or hex #rrggbb) for an inactive 427 | window with _NET_WM_STATE_DEMANDS_ATTENTION (default: Red). 428 | .RS 429 | .PP 430 | goomwwm -attention Red 431 | .RE 432 | .TP 433 | .B -auto 434 | Search for an app at startup and autostart it if not found. 435 | Uses the same WM_CLASS/name/title matching rules as the -1 through -9 436 | arguments (default: none). 437 | .RS 438 | .PP 439 | goomwwm -auto chromium 440 | .PP 441 | Above, chromium will only be started if a chromium window does not 442 | already exist. 443 | .RE 444 | .TP 445 | .B -below 446 | Set an X11 key name to toggle _NET_WM_STATE_BELOW for the active window 447 | (default: XK_b). 448 | .RS 449 | .PP 450 | goomwwm -below b 451 | .RE 452 | .TP 453 | .B -blur 454 | Set the border color (X11 named color or hex #rrggbb) for unfocused 455 | windows (default: Dark Gray). 456 | .RS 457 | .PP 458 | goomwwm -blur "Dark Gray" 459 | .RE 460 | .TP 461 | .B -border 462 | Set the border width in pixels for all managed windows (default: 2). 463 | .RS 464 | .PP 465 | goomwwm -border 2 466 | .RE 467 | .TP 468 | .B -close 469 | Set an X11 key name to gracefully close the active window (default: 470 | XK_Escape). 471 | .RS 472 | .PP 473 | goomwwm -close Escape 474 | .RE 475 | .TP 476 | .B -config 477 | Parse extra options from a text file. 478 | .RS 479 | .PP 480 | goomwwm -config /path/to/config.txt 481 | .PP 482 | The file format is any command line options without the leading hyphen. 483 | Comments and blank lines are acceptable. 484 | .IP 485 | .nf 486 | \f[C] 487 | #\ a\ comment 488 | 2\ chromium 489 | 2\ konsole 490 | close\ Escape 491 | menufont\ mono-14 492 | \f[] 493 | .fi 494 | .PP 495 | If it exists, \f[B]$HOME/.goomwwmrc\f[] is automatically parsed. 496 | .RE 497 | .TP 498 | .B -cycle 499 | Set an X11 key name to cycle windows in the same tag and position as the 500 | active window (default: XK_c). 501 | .RS 502 | .PP 503 | goomwwm -cycle c 504 | .RE 505 | .TP 506 | .B -contract 507 | Set an X11 key name to contract the active window to fill an underlying 508 | space without obscuring any other window in the current tag that would 509 | then be fully visible (default: XK_Contract). 510 | Opposite of -expand. 511 | .RS 512 | .PP 513 | goomwwm -contract BackSpace 514 | .RE 515 | .TP 516 | .B -decrease 517 | Set an X11 key to decrementally resize the active window (default: 518 | shift+page_up). 519 | .RS 520 | .PP 521 | goomwwm -decrease shift+page_down 522 | .RE 523 | .TP 524 | .B -down 525 | Set an X11 key name to move the active window downward in a 3x3 grid 526 | (default: XK_Down). 527 | .RS 528 | .PP 529 | goomwwm -down Down 530 | .RE 531 | .TP 532 | .B -duplicate 533 | Set an X11 key name to resize the active window to match the window 534 | immediately underneath, in the same tag (default: XK_d). 535 | .RS 536 | .PP 537 | goomwwm -duplicate d 538 | .RE 539 | .TP 540 | .B -exec 541 | Execute a command at startup but only after goomwwm has started 542 | successfully (default: none). 543 | Useful for pre-lanching apps, but also see \f[B]-auto\f[]. 544 | .RS 545 | .PP 546 | goomwwm -exec firefox 547 | .RE 548 | .TP 549 | .B -expand 550 | Set an X11 key name to expand the active window to fill adjacent space 551 | without obscuring any other fully visible window in the current tag 552 | (default: XK_Return). 553 | Opposite of -contract. 554 | .RS 555 | .PP 556 | goomwwm -expand Return 557 | .RE 558 | .TP 559 | .B -largeleft 560 | Set an X11 key to move and resize the active window to cover the left 561 | 2/3 of a monitor (default: XK_bracketleft). 562 | .RS 563 | .PP 564 | goomwwm -largeleft bracketleft 565 | .RE 566 | .TP 567 | .B -largeright 568 | Set an X11 key to move and resize the active window to cover the right 569 | 2/3 of a monitor (default: XK_bracketright). 570 | .RS 571 | .PP 572 | goomwwm -largeleft bracketleft 573 | .RE 574 | .TP 575 | .B -launch 576 | Set an X11 key to run the application launcher (default: XK_x). 577 | .RS 578 | .PP 579 | goomwwm -launch x 580 | .RE 581 | .TP 582 | .B -launcher 583 | Set a custom application launcher to execute on \f[B]Mod-x\f[] (default: 584 | dmenu_run). 585 | .RS 586 | .PP 587 | goomwwm -launcher dmenu_run 588 | .RE 589 | .TP 590 | .B -flashms 591 | Set the duration in milliseconds of the window flash indicators 592 | (default: 500). 593 | .RS 594 | .PP 595 | goomwwm -flashms 500 596 | .RE 597 | .TP 598 | .B -flashon 599 | Set the color (X11 named color or hex #rrggbb) of the flash indicator 600 | when toggling \f[I]NET_WM_STATE\f[]* on (default: Dark Green). 601 | .RS 602 | .PP 603 | goomwwm -flashon "Dark Green" 604 | .RE 605 | .TP 606 | .B -flashoff 607 | Set the color (X11 named color or hex #rrggbb) of the flash indicator 608 | when toggling \f[I]NET_WM_STATE\f[]* off (default: Dark Red). 609 | .RS 610 | .PP 611 | goomwwm -flashon "Dark Red" 612 | .RE 613 | .TP 614 | .B -flashpx 615 | Set the size in pixels of window flash indicators (currently a colored 616 | square in each window corner) (default: 10). 617 | .RS 618 | .PP 619 | goomwwm -flashpx 10 620 | .RE 621 | .TP 622 | .B -flashtitle 623 | Wether to flash a window\[aq]s title when changing focus or other modes 624 | (default: hide). 625 | Regardless of this setting, \f[B]Mod-w\f[] always displays a 626 | window\[aq]s title. 627 | .RS 628 | .PP 629 | goomwwm -flashtitle hide 630 | .PP 631 | Valid modes: 632 | .TP 633 | .B show 634 | Flash title bar centered on the window. 635 | .RS 636 | .RE 637 | .TP 638 | .B hide 639 | Do no flash title bar. 640 | .RS 641 | .RE 642 | .RE 643 | .TP 644 | .B -focus 645 | Set the border color (X11 named color or hex #rrggbb) for the focused 646 | window (default: Royal Blue). 647 | .RS 648 | .PP 649 | goomwwm -focus "Royal Blue" 650 | .RE 651 | .TP 652 | .B -focusdown 653 | Set an X11 key name to switch focus downward from the active window 654 | within the current tag (default: XK_k). 655 | .RS 656 | .PP 657 | goomwwm -focusdown k 658 | .RE 659 | .TP 660 | .B -focusleft 661 | Set an X11 key name to switch focus to left of the active window within 662 | the current tag (default: XK_j). 663 | .RS 664 | .PP 665 | goomwwm -focusleft j 666 | .RE 667 | .TP 668 | .B -focusmode 669 | Control the window focus mode (default: click). 670 | .RS 671 | .PP 672 | goomwwm -focusmode click 673 | .PP 674 | Valid settings are: 675 | .TP 676 | .B click 677 | focus on mouse click. 678 | .RS 679 | .RE 680 | .TP 681 | .B sloppy 682 | focus follows mouse 683 | .RS 684 | .RE 685 | .TP 686 | .B sloppytag 687 | focus follows mouse within current tag. 688 | .RS 689 | .RE 690 | .RE 691 | .TP 692 | .B -focusright 693 | Set an X11 key name to switch focus to right of the active window within 694 | the current tag (default: XK_l). 695 | .RS 696 | .PP 697 | goomwwm -focusright l 698 | .RE 699 | .TP 700 | .B -focusup 701 | Set an X11 key name to switch focus upward form the active window within 702 | the current tag (default: XK_i). 703 | .RS 704 | .PP 705 | goomwwm -focusup i 706 | .RE 707 | .TP 708 | .B -fullscreen 709 | Set an X11 key name to toggle _NET_WM_STATE_FULLSCREEN for the active 710 | window (default: XK_f). 711 | .RS 712 | .PP 713 | goomwwm -fullscreen f 714 | .RE 715 | .TP 716 | .B -grow 717 | Set an X11 key name to increase the active window size (default: 718 | XK_Page_Up) through four basic sizes that tile well together: 1/9th, 719 | 1/4th, 4/9th, or fullscreen. 720 | .RS 721 | .PP 722 | goomwwm -grow Page_Up 723 | .RE 724 | .TP 725 | .B -hlock 726 | Set an X11 key name to toggle horizontal move/resize lock for the active 727 | window (default: XK_Delete). 728 | .RS 729 | .PP 730 | goomwwm -hlock Delete 731 | .RE 732 | .TP 733 | .B -hmax 734 | Set an X11 key name to toggle _NET_WM_STATE_MAXIMIXED_HORZ for the 735 | active window (default: XK_End). 736 | .RS 737 | .PP 738 | goomwwm -hmax End 739 | .RE 740 | .TP 741 | .B -htile 742 | Set an X11 key to horizontally tile the active window and others with 743 | the same tag, position, and size (default: XK_h). 744 | .RS 745 | .PP 746 | goomwwm -htile h 747 | .RE 748 | .TP 749 | .B -huntile 750 | Set an X11 key to do the opposite of -htile. 751 | .RS 752 | .PP 753 | goomwwm -huntile h 754 | .RE 755 | .TP 756 | .B -info 757 | Set an X11 key to briefly display the active window\[aq]s title 758 | (default: XK_w). 759 | .RS 760 | .PP 761 | goomwwm -info w 762 | .RE 763 | .TP 764 | .B -increase 765 | Set an X11 key to incrementally resize the active window (default: 766 | shift+page_up). 767 | .RS 768 | .PP 769 | goomwwm -increase shift+page_up 770 | .RE 771 | .TP 772 | .B -left 773 | Set an X11 key name to move the active window to the left in a 3x3 grid 774 | (default: XK_Left). 775 | .RS 776 | .PP 777 | goomwwm -left Left 778 | .RE 779 | .TP 780 | .B -mapmode 781 | Control the window initial map focus mode (default: steal). 782 | .RS 783 | .PP 784 | goomwwm -mapmode steal 785 | .PP 786 | Valid settings are: 787 | .TP 788 | .B steal 789 | new windows get focus. 790 | .RS 791 | .RE 792 | .TP 793 | .B block 794 | new windows do not get focus. 795 | .RS 796 | .RE 797 | .RE 798 | .TP 799 | .B -menubc 800 | Set the border color (X11 named color or hex #rrggbb) for the 801 | window-switcher menu (default: #c0c0c0). 802 | .RS 803 | .PP 804 | goomwwm -menubc "#c0c0c0" 805 | .RE 806 | .TP 807 | .B -menubg 808 | Set the background text color (X11 named color or hex #rrggbb) for the 809 | window-switcher menu (default: #f2f1f0). 810 | .RS 811 | .PP 812 | goomwwm -menubg "#f2f1f0" 813 | .RE 814 | .TP 815 | .B -menubgalt 816 | Set the alternate background text color (X11 named color or hex #rrggbb) 817 | for the window-switcher menu (default: #e9e8e7). 818 | .RS 819 | .PP 820 | goomwwm -menubgalt "#e9e8e7" 821 | .RE 822 | .TP 823 | .B -menufg 824 | Set the foreground text color (X11 named color or hex #rrggbb) for the 825 | window-switcher menu (default: #222222). 826 | .RS 827 | .PP 828 | goomwwm -menufg "#222222" 829 | .RE 830 | .TP 831 | .B -menufont 832 | Xft font name for use by the window-switcher menu (default: mono-14). 833 | .RS 834 | .PP 835 | goomwwm -menufont monospace-14:medium 836 | .RE 837 | .TP 838 | .B -menuhlbg 839 | Set the background text color (X11 named color or hex #rrggbb) for the 840 | highlighted item in the window-switcher menu (default: #005577). 841 | .RS 842 | .PP 843 | goomwwm -menufg "#005577" 844 | .RE 845 | .TP 846 | .B -menuhlfg 847 | Set the foreground text color (X11 named color or hex #rrggbb) for the 848 | highlighted item in the window-switcher menu (default: #ffffff). 849 | .RS 850 | .PP 851 | goomwwm -menufg "#ffffff" 852 | .RE 853 | .TP 854 | .B -menulines 855 | Maximum number of entries the window-switcher menu may show before 856 | scrolling (default: 25). 857 | .RS 858 | .PP 859 | goomwwm -menulines 25 860 | .RE 861 | .TP 862 | .B -menuselect 863 | Control how menu items are selected (default: return). 864 | .RS 865 | .PP 866 | goomwwm -menuselect return 867 | .PP 868 | Valid settings are: 869 | .TP 870 | .B return 871 | Menu stays open until item is selected with Enter/Return key. 872 | This is dmenu-like. 873 | .RS 874 | .RE 875 | .TP 876 | .B modkeyup 877 | Menu stays open until item is selected by releasing the modkey. 878 | This is classic Alt-Tab window switching behavior. 879 | .RS 880 | .RE 881 | .RE 882 | .TP 883 | .B -menuwidth 884 | Set the width of the window-switcher menu as a percentage of the screen 885 | width if <= 100 (% symbol optional), or in pixels if >100 (default: 886 | 60%). 887 | .RS 888 | .PP 889 | goomwwm -menuwidth 60% goomwwm -menuwidth 800 890 | .RE 891 | .TP 892 | .B -minimize 893 | Set an X11 key name to minimize a window (default: XK_slash). 894 | .RS 895 | .PP 896 | goomwwm -minimize slash 897 | .RE 898 | .TP 899 | .B -modkey 900 | Change the modifier key mask to any combination of: 901 | control,mod1,mod2,mod3,mod4,mod5 (default: mod4). 902 | .RS 903 | .PP 904 | goomwwm -modkey control,mod1 905 | .RE 906 | .TP 907 | .B -only 908 | Set an X11 key name to show only windows in the current tag, hiding 909 | everything else (default: XK_o). 910 | .RS 911 | .PP 912 | goomwwm -only o 913 | .RE 914 | .TP 915 | .B -onlyauto 916 | Make \f[B]-only\f[] behavior automatic after current tag switch. 917 | Note that while this setting makes tags behave pretty much like virtual 918 | desktops, it also reduces flexibility. 919 | .RS 920 | .PP 921 | goomwwm -onlyauto 922 | .RE 923 | .TP 924 | .B -placement 925 | Control the position of new windows (default: any). 926 | .RS 927 | .PP 928 | goomwwm -placement any 929 | .PP 930 | Valid settings are: 931 | .TP 932 | .B any 933 | Windows that specify or remember their placement are honored. 934 | Everything else is centered on the current monitor. 935 | .RS 936 | .RE 937 | .TP 938 | .B center 939 | Windows are centered on the current monitor. 940 | .RS 941 | .RE 942 | .TP 943 | .B pointer 944 | Windows are centered under the mouse pointer. 945 | .RS 946 | .RE 947 | .RE 948 | .TP 949 | .B -prefix 950 | Set an X11 key name to act as a modal key combination that replaces the 951 | default modifier key for all other combinations (default: none). 952 | This is similar to the way key combinations work in \f[B]ratpoison\f[] 953 | and GNU \f[B]screen\f[]. 954 | .RS 955 | .PP 956 | goomwwm -prefix z 957 | .PP 958 | Above, \f[B]Mod-z\f[] would now need to preceed all other keys. 959 | For example, cycling windows would become preass and relases 960 | \f[B]Mod-z\f[] then press \f[B]c\f[]. 961 | .PP 962 | Of course, \f[B]-prefix\f[] can also be combined with \f[B]-modkey\f[]: 963 | .PP 964 | goomwwm -modkey control -prefix z 965 | .PP 966 | Cycling windows would then become \f[B]Control-z\f[] then \f[B]c\f[]. 967 | .PP 968 | Finally, if you press the prefix key combination by mistake, press the 969 | prefix key again to cancel. 970 | .RE 971 | .TP 972 | .B -quit 973 | Set an X11 key name to exit the window manager (default: XK_Pause). 974 | This key must be \f[B]pressed twice\f[] to take effect! 975 | .RS 976 | .PP 977 | goomwwm -quit Pause 978 | .RE 979 | .TP 980 | .B -raisemode 981 | Control the window raise mode (default: focus). 982 | .RS 983 | .PP 984 | goomwwm -raisemode focus 985 | .PP 986 | Valid settings are: 987 | .TP 988 | .B focus 989 | Window is raised on focus (default for -focusmode click). 990 | .RS 991 | .RE 992 | .TP 993 | .B click 994 | Window is raised on Mod-AnyButton click (default for -focusmode 995 | sloppy[tag]). 996 | .RS 997 | .RE 998 | .RE 999 | .TP 1000 | .B -resizehints 1001 | How to handle windows that specify resize-increment hints (Default: 1002 | smart). 1003 | These are what can sometimes cause tiled terminals to have gaps around 1004 | the edges. 1005 | .RS 1006 | .PP 1007 | goomwwm -resizehints smart 1008 | .PP 1009 | Valid settings are: 1010 | .TP 1011 | .B all 1012 | All window hints are respected. 1013 | .RS 1014 | .RE 1015 | .TP 1016 | .B none 1017 | No window hints are respected. 1018 | Note that this does not prevent windows from sending a follow-up request 1019 | to be resized to respect their hints. 1020 | gnome-terminal and lxterminal both do this and may always show gaps. 1021 | .RS 1022 | .RE 1023 | .TP 1024 | .B smart 1025 | Most window hints are respected, except for a few apps we know can 1026 | handle having their hints ignored. 1027 | At present, this is \f[B]xterm\f[] and \f[B]urxvt\f[]. 1028 | .RS 1029 | .RE 1030 | .TP 1031 | .B (posix regex) 1032 | Implies smart mode. 1033 | A regular expression to match the WM_CLASS of windows to ignore 1034 | \f[B]smart\f[] mode. 1035 | By default this is "^(xterm|urxvt)$". 1036 | Regex is case-insensitive using POSIX extended syntax. 1037 | .RS 1038 | .RE 1039 | .RE 1040 | .TP 1041 | .B -right 1042 | Set an X11 key name to move the active window to the right in a 3x3 grid 1043 | (default: XK_Right). 1044 | .RS 1045 | .PP 1046 | goomwwm -right Right 1047 | .RE 1048 | .TP 1049 | .B -rule 1050 | Define a global window control rule (default: none). 1051 | This argument can be specified multiple times to define multiple rules. 1052 | If a window matches multiple rules only the \f[I]last\f[] rule specified 1053 | is used. 1054 | .RS 1055 | .IP 1056 | .nf 1057 | \f[C] 1058 | goomwwm\ -rule\ "firefox\ tag9" 1059 | goomwwm\ -rule\ "xfce4-notifyd\ ignore" 1060 | goomwwm\ -rule\ "xterm\ left,maximize_vert,medium" 1061 | \f[] 1062 | .fi 1063 | .PP 1064 | Rules always have the format: 1065 | .IP 1066 | .nf 1067 | \f[C] 1068 | pattern\ flag[...,flagN] 1069 | \f[] 1070 | .fi 1071 | .PP 1072 | The \f[B]pattern\f[] is a case-insensitive POSIX regular expression 1073 | matched against a window\[aq]s WM_CLASS, application name, or title 1074 | fields (in that order). 1075 | Alternatively, the pattern can be limited to one field by using 1076 | \f[B]class:\f[], \f[B]name:\f[], or \f[B]title:\f[] pattern prefixes 1077 | (this is also faster): 1078 | .IP 1079 | .nf 1080 | \f[C] 1081 | goomwwm\ -rule\ "class:firefox\ tag9" 1082 | goomwwm\ -rule\ "name:xfce4-notifyd\ ignore" 1083 | goomwwm\ -rule\ "title:xterm\ left,maximize_vert,medium" 1084 | \f[] 1085 | .fi 1086 | .PP 1087 | Valid \f[B]flags\f[] are: 1088 | .TP 1089 | .B ignore 1090 | Do not manage a window. 1091 | Effectively makes a window behave as it the override_redirect flag is 1092 | set. 1093 | .RS 1094 | .RE 1095 | .TP 1096 | .B steal block 1097 | Allow or prevent a new widow taking focus. 1098 | .RS 1099 | .RE 1100 | .TP 1101 | .B reset 1102 | Remove all EWMH states and H/V locks (useful for -ruleset). 1103 | .RS 1104 | .RE 1105 | .TP 1106 | .B once 1107 | Allow a rule to execute only once (useful for -ruleset). 1108 | .RS 1109 | .RE 1110 | .TP 1111 | .B minimize restore 1112 | Start window pre-minimzed, or restore a window on rule set switch. 1113 | .RS 1114 | .RE 1115 | .TP 1116 | .B minimize_auto 1117 | Automatically minimize a window when it loses focus. 1118 | .RS 1119 | .RE 1120 | .TP 1121 | .B tag1 tag2 tag3 tag4 tag5 tag6 tag7 tag8 tag9 1122 | Apply tags to a window when it first opens. 1123 | If the current tag is not in the list the window will not be raised or 1124 | allowed to take focus. 1125 | .RS 1126 | .RE 1127 | .TP 1128 | .B monitor1 monitor2 monitor3 1129 | Place the window on a specific monitor. 1130 | These are numbered based on what Xinerama thinks the monitor order 1131 | should be (ie, usually the same screen numbers as defined in xorg.conf). 1132 | .RS 1133 | .RE 1134 | .TP 1135 | .B above below fullscreen maximize_horz maximize_vert sticky 1136 | skip_taskbar skip_pager 1137 | Apply respective \f[I]NET_WM_STATE\f[]* to a window. 1138 | .RS 1139 | .RE 1140 | .TP 1141 | .B raise lower 1142 | Pre-raise or lower a window in the stacking order. 1143 | These only take effect for a blocked window. 1144 | For unblocked windows that take focus, -raisemode takes precedence. 1145 | .RS 1146 | .RE 1147 | .TP 1148 | .B left right top bottom 1149 | Align a window with a screen edge. 1150 | May be combined. 1151 | Top trumps bottom. 1152 | Left trumps right. 1153 | .RS 1154 | .RE 1155 | .TP 1156 | .B center pointer 1157 | Place a window center-screen or centered under the mouse pointer. 1158 | .RS 1159 | .RE 1160 | .TP 1161 | .B small medium large cover expand contract 1162 | Set a window\[aq]s initial size (same increments as PageUp/Down). 1163 | May be combined. 1164 | .RS 1165 | .RE 1166 | .TP 1167 | .B hlock vlock 1168 | Lock window horizontally or vertically. 1169 | .RS 1170 | .RE 1171 | .TP 1172 | .B htile huntile vtile vuntile 1173 | Tile or untile a window with its fellows. 1174 | .RS 1175 | .RE 1176 | .TP 1177 | .B snap_left snap_right snap_up snap_down 1178 | Immediately snap a window to another\[aq]s edge. 1179 | .RS 1180 | .RE 1181 | .TP 1182 | .B replace 1183 | Place a window in the same position as the active window. 1184 | .RS 1185 | .RE 1186 | .TP 1187 | .B duplicate 1188 | Size a window to match the one beneath it. 1189 | .RS 1190 | .RE 1191 | .TP 1192 | .B NxN N%xN% 1193 | Apply a specific size in pixels or percent of monitor size. 1194 | .RS 1195 | .RE 1196 | .PP 1197 | Rules are not currently applied to transient windows (dialogs). 1198 | .RE 1199 | .TP 1200 | .B -ruleset 1201 | Define a group of rules to execute on all windows in the current tag 1202 | when selected by menu (default: none). 1203 | See \f[B]-runruleset\f[]. 1204 | .RS 1205 | .PP 1206 | goomwwm -ruleset Name -rule ... 1207 | -rule ... 1208 | -ruleset Name2 -rule ... 1209 | .PP 1210 | Or, in .goomwwmrc: 1211 | .IP 1212 | .nf 1213 | \f[C] 1214 | ruleset\ Development\ Layout 1215 | rule\ class:xterm\ right,bottom,small 1216 | rule\ class:gvim\ left,maximize_vert,large 1217 | 1218 | ruleset\ Email/Chat\ Distractions 1219 | rule\ class:pidgin\ left,bottom,small,snap_right 1220 | rule\ class:chromium\ top,maximize_horz,large 1221 | \f[] 1222 | .fi 1223 | .PP 1224 | All \f[B]-ruleset\f[] definitions need to come after the global 1225 | \f[B]-rule\f[] definitions on the command line, or in .goomwwmrc. 1226 | .PP 1227 | Where global rules are autonomous and their order is not important, 1228 | rulesets are more like mini scripts where rules are commands executed in 1229 | order. 1230 | Windows may therefore be affected by multiple rules in a ruleset. 1231 | Use precise regex patterns to be safe. 1232 | .RE 1233 | .TP 1234 | .B -runrule 1235 | Set an X11 key name to reapply any rule relevant to the active window 1236 | (default: XK_comma). 1237 | .RS 1238 | .PP 1239 | goomwwm -runrule comma 1240 | .RE 1241 | .TP 1242 | .B -runruleset 1243 | Set an X11 key name to execute defined rule sets using a menu (default: 1244 | XK_period). 1245 | .RS 1246 | .PP 1247 | goomwwm -runrule period 1248 | .RE 1249 | .TP 1250 | .B -shrink 1251 | Set an X11 key name to decrease the active window size (default: 1252 | XK_Page_Down) through four basic sizes that tile well together: 1/9th, 1253 | 1/4th, 4/9th, or fullscreen. 1254 | .RS 1255 | .PP 1256 | goomwwm -shrink Page_Down 1257 | .RE 1258 | .TP 1259 | .B -snapdown 1260 | Set an X11 key name to snap the active window downward to the nearest 1261 | border. 1262 | .RS 1263 | .PP 1264 | goomwwm -snapdown Shift+Down 1265 | .RE 1266 | .TP 1267 | .B -snapleft 1268 | Set an X11 key name to snap the active window left to the nearest 1269 | border. 1270 | .RS 1271 | .PP 1272 | goomwwm -snapleft Shift+Left 1273 | .RE 1274 | .TP 1275 | .B -snapright 1276 | Set an X11 key name to snap the active window right to the nearest 1277 | border. 1278 | .RS 1279 | .PP 1280 | goomwwm -snapright Shift+Right 1281 | .RE 1282 | .TP 1283 | .B -snapup 1284 | Set an X11 key name to snap the active window upward to the nearest 1285 | border. 1286 | .RS 1287 | .PP 1288 | goomwwm -snapup Shift+Up 1289 | .RE 1290 | .TP 1291 | .B -swapdown 1292 | Set an X11 key name to swap the active window with one below. 1293 | .RS 1294 | .PP 1295 | goomwwm -swapdown Shift+Down 1296 | .RE 1297 | .TP 1298 | .B -swapleft 1299 | Set an X11 key name to swap the active window with one to the left. 1300 | .RS 1301 | .PP 1302 | goomwwm -swapleft Shift+Left 1303 | .RE 1304 | .TP 1305 | .B -swapright 1306 | Set an X11 key name to swap the active window with one to the right. 1307 | .RS 1308 | .PP 1309 | goomwwm -swapright Shift+Right 1310 | .RE 1311 | .TP 1312 | .B -swapup 1313 | Set an X11 key name to swap the active window with one above. 1314 | .RS 1315 | .PP 1316 | goomwwm -swapup Shift+Up 1317 | .RE 1318 | .TP 1319 | .B -switch 1320 | Set an X11 key to start display window-switcher showing all open windows 1321 | (default: XK_Tab). 1322 | .RS 1323 | .PP 1324 | goomwwm -switch Tab 1325 | .RE 1326 | .TP 1327 | .B -switcher 1328 | Command to run an alternate window-switcher (default: built-in menu). 1329 | .RS 1330 | .PP 1331 | goomwwm -switcher dswitch 1332 | .RE 1333 | .TP 1334 | .B -tag 1335 | Set an X11 key to toggle the active window\[aq]s membership of the 1336 | current tag (default: XK_t). 1337 | .RS 1338 | .PP 1339 | goomwwm -tag t 1340 | .RE 1341 | .TP 1342 | .B -tswitch 1343 | Set an X11 key to start display window-switcher showing only windows in 1344 | the current tag (default: XK_grave). 1345 | .RS 1346 | .PP 1347 | goomwwm -tswitch grave 1348 | .RE 1349 | .TP 1350 | .B -tagnext 1351 | Set an X11 key to cycle tags forward (default: XK_m). 1352 | .RS 1353 | .PP 1354 | goomwwm -tagnext m 1355 | .RE 1356 | .TP 1357 | .B -tagprev 1358 | Set an X11 key to cycle tags in reverse (default: XK_n). 1359 | .RS 1360 | .PP 1361 | goomwwm -tagprev n 1362 | .RE 1363 | .TP 1364 | .B -titlebc 1365 | Set the border color (X11 named color or hex #rrggbb) for window titles 1366 | (default: #c0c0c0). 1367 | .RS 1368 | .PP 1369 | goomwwm -titlebc "#c0c0c0" 1370 | .RE 1371 | .TP 1372 | .B -titlebg 1373 | Set the background text color (X11 named color or hex #rrggbb) for 1374 | window titles (default: #f2f1f0). 1375 | .RS 1376 | .PP 1377 | goomwwm -titlebg "#f2f1f0" 1378 | .RE 1379 | .TP 1380 | .B -titlefg 1381 | Set the foreground text color (X11 named color or hex #rrggbb) for 1382 | window titles (default: #222222). 1383 | .RS 1384 | .PP 1385 | goomwwm -titlefg "#222222" 1386 | .RE 1387 | .TP 1388 | .B -titlefont 1389 | Xft font name for use by window popup titles (default: sans-14). 1390 | .RS 1391 | .PP 1392 | goomwwm -titlefont sans-14:medium 1393 | .RE 1394 | .TP 1395 | .B -titlebar 1396 | Height in pixels of a window title bar or on/off (default: off). 1397 | .RS 1398 | .PP 1399 | goomwwm -titlebar 18 goomwwm -titlebar on goomwwm -titlebar off 1400 | .PP 1401 | The \f[B]on\f[] argument will auto-calculate titlebar height based on 1402 | \f[B]-titlebarfont\f[]. 1403 | .RE 1404 | .TP 1405 | .B -titlebarfont 1406 | Xft font name for use by window title bars (default: sans-10). 1407 | .RS 1408 | .PP 1409 | goomwwm -titlebarfont sans-10:medium 1410 | .RE 1411 | .TP 1412 | .B -titlebarfocus 1413 | Set the text color of focused-window title bars (X11 named color or hex 1414 | #rrggbb). 1415 | .RS 1416 | .PP 1417 | goomwwm -titlebarfocus "#eeeeee" 1418 | .RE 1419 | .TP 1420 | .B -titlebarblur 1421 | Set the text color of unfocused-window title bars (X11 named color or 1422 | hex #rrggbb). 1423 | .RS 1424 | .PP 1425 | goomwwm -titlebarblur "#eeeeee" 1426 | .RE 1427 | .TP 1428 | .B -up 1429 | Set an X11 key name to move the active window upward in a 3x3 grid 1430 | (default: XK_Up). 1431 | .RS 1432 | .PP 1433 | goomwwm -up Up 1434 | .RE 1435 | .TP 1436 | .B -undo 1437 | Set an X11 key to undo the last size/position change for the active 1438 | window (default: XK_u). 1439 | Undo is 10 levels deep. 1440 | .RS 1441 | .PP 1442 | goomwwm -undo u 1443 | .RE 1444 | .TP 1445 | .B -vlock 1446 | Set an X11 key name to toggle vertical move/resize lock for the active 1447 | window (default: XK_Insert). 1448 | .RS 1449 | .PP 1450 | goomwwm -vlock Insert 1451 | .RE 1452 | .TP 1453 | .B -vmax 1454 | Set an X11 key name to toggle _NET_WM_STATE_MAXIMIXED_VERT for the 1455 | active window (default: XK_Home). 1456 | .RS 1457 | .PP 1458 | goomwwm -vmax Home 1459 | .RE 1460 | .TP 1461 | .B -vtile 1462 | Set an X11 key to vertically tile the active window and other windows 1463 | with the same tag, position, and size (default: XK_v). 1464 | .RS 1465 | .PP 1466 | goomwwm -vtile h 1467 | .RE 1468 | .TP 1469 | .B -vuntile 1470 | Set an X11 key to do the opposite of vtile. 1471 | .RS 1472 | .PP 1473 | goomwwm -vuntile h 1474 | .RE 1475 | .TP 1476 | .B -warpmode 1477 | Control whether the mouse pointer warps to a focused window (default: 1478 | never). 1479 | This setting can make focusmode \f[B]sloppy\f[] more cooperative when 1480 | focus is changed by means other than the mouse. 1481 | .RS 1482 | .PP 1483 | goomwwm -warpmode focus 1484 | .PP 1485 | Valid settings are: 1486 | .TP 1487 | .B never 1488 | Pointer is never moved (default for -focusmode click). 1489 | .RS 1490 | .RE 1491 | .TP 1492 | .B follow 1493 | Pointer follows a moved window, but is not moved to a newly focused window. 1494 | .RS 1495 | .RE 1496 | .TP 1497 | .B focus 1498 | Pointer is warped to a newly focused window, and follows a moved window 1499 | (default for -focusmode sloppy[tag]). 1500 | .RS 1501 | .RE 1502 | .RE 1503 | .SH OPTIONS (cli mode) 1504 | .PP 1505 | When run with \f[B]-cli\f[] (command line interface) goomwwm may be used 1506 | to dispatch commands to another running instance of goomwwm. 1507 | Valid arguments are: 1508 | .TP 1509 | .B -duration 1510 | A time delay in seconds. 1511 | Currently used only for \f[B]-notice\f[]. 1512 | .RS 1513 | .PP 1514 | goomwwm -cli -notice "Hello World" -duration 5 1515 | .RE 1516 | .TP 1517 | .B -exec 1518 | Switch to another window manager in place (without restarting X). 1519 | .RS 1520 | .PP 1521 | goomwwm -cli -exec dwm 1522 | .RE 1523 | .TP 1524 | .B -findstart 1525 | Locate a window by class, name, or title. 1526 | If not found, execute it. 1527 | .RS 1528 | .PP 1529 | goomwwm -cli -findstart class:xterm 1530 | .RE 1531 | .TP 1532 | .B -notice 1533 | Instruct goomwwm to display something via the popup message box. 1534 | .RS 1535 | .PP 1536 | goomwwm -cli -notice "Hello World" 1537 | .RE 1538 | .TP 1539 | .B -quit 1540 | Exit goomwwm. 1541 | .RS 1542 | .PP 1543 | goomwwm -cli -quit 1544 | .RE 1545 | .TP 1546 | .B -restart 1547 | Restart a running goomwwm instance in place (without restarting X). 1548 | Useful for reloading .goomwwmrc or upgrading to a new version. 1549 | .RS 1550 | .PP 1551 | goomwwm -cli -restart 1552 | .RE 1553 | .TP 1554 | .B -rule 1555 | Execute a temporary rule on windows in the current tag. 1556 | .RS 1557 | .PP 1558 | goomwwm -cli -rule "xterm large" 1559 | .RE 1560 | .TP 1561 | .B -ruleset 1562 | Execute a rule set by name. 1563 | .RS 1564 | .PP 1565 | goomwwm -cli -ruleset alpha 1566 | .RE 1567 | .SH SEE ALSO 1568 | .PP 1569 | \f[B]dmenu\f[] (1) 1570 | .SH AUTHOR 1571 | .PP 1572 | Sean Pringle 1573 | -------------------------------------------------------------------------------- /goomwwm.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | #define _GNU_SOURCE 28 | #include "version.h" 29 | #include "goomwwm.h" 30 | #include "proto.h" 31 | #include "util.c" 32 | #include "box.c" 33 | #include "textbox.c" 34 | #include "winlist.c" 35 | #include "rule.c" 36 | #include "window.c" 37 | #include "monitor.c" 38 | #include "client.c" 39 | #include "ewmh.c" 40 | #include "tag.c" 41 | #include "menu.c" 42 | #include "handle.c" 43 | #include "grab.c" 44 | #include "cli.c" 45 | #include "wm.c" 46 | 47 | int main(int argc, char *argv[]) 48 | { 49 | int i; 50 | 51 | // catch help request 52 | if (find_arg(argc, argv, "-help") >= 0 53 | || find_arg(argc, argv, "--help") >= 0 54 | || find_arg(argc, argv, "-h") >= 0) 55 | { 56 | fprintf(stderr, "See the man page or visit http://github.com/seanpringle/goomwwm\n"); 57 | return EXIT_SUCCESS; 58 | } 59 | 60 | // catch version request 61 | if (find_arg(argc, argv, "-version") >= 0 62 | || find_arg(argc, argv, "--version") >= 0 63 | || find_arg(argc, argv, "-v") >= 0) 64 | { 65 | fprintf(stderr, "%s\n", VERSION); 66 | return EXIT_SUCCESS; 67 | } 68 | 69 | if(!(display = XOpenDisplay(0))) 70 | { 71 | fprintf(stderr, "cannot open display!\n"); 72 | return EXIT_FAILURE; 73 | } 74 | signal(SIGCHLD, catch_exit); 75 | screen = DefaultScreenOfDisplay(display); 76 | screen_id = DefaultScreen(display); 77 | root = DefaultRootWindow(display); 78 | 79 | // X atom values 80 | for (i = 0; i < ATOMS; i++) atoms[i] = XInternAtom(display, atom_names[i], False); 81 | for (i = 0; i < GATOMS; i++) gatoms[i] = XInternAtom(display, gatom_names[i], False); 82 | for (i = 0; i < NETATOMS; i++) netatoms[i] = XInternAtom(display, netatom_names[i], False); 83 | 84 | return find_arg(argc, argv, "-cli") >= 0 ? cli_main(argc, argv): wm_main(argc, argv); 85 | } 86 | -------------------------------------------------------------------------------- /goomwwm.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Encoding=UTF-8 3 | Name=goomwwm 4 | Comment=Get out of my way, Window Manager! 5 | Exec=goomwwm 6 | Type=XSession 7 | -------------------------------------------------------------------------------- /goomwwm.h: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | typedef unsigned char bool; 53 | typedef unsigned long long bitmap; 54 | 55 | #define BOX_OVERRIDE 1<<0 56 | 57 | typedef struct { 58 | bitmap flags; 59 | Window window, parent; 60 | short x, y, w, h; 61 | unsigned int color; 62 | } box; 63 | 64 | #define TB_AUTOHEIGHT 1<<0 65 | #define TB_AUTOWIDTH 1<<1 66 | #define TB_LEFT 1<<16 67 | #define TB_RIGHT 1<<17 68 | #define TB_CENTER 1<<18 69 | #define TB_EDITABLE 1<<19 70 | 71 | typedef struct { 72 | bitmap flags; 73 | Window window, parent; 74 | short x, y, w, h; 75 | short cursor; 76 | XftFont *font; 77 | XftColor color_fg, color_bg; 78 | char *text, *prompt; 79 | XIM xim; 80 | XIC xic; 81 | XGlyphInfo extents; 82 | } textbox; 83 | 84 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 85 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 86 | #define READ 0 87 | #define WRITE 1 88 | 89 | #define NEAR(a,o,b) ((b) > (a)-(o) && (b) < (a)+(o)) 90 | #define SNAPTO(a,o,b,j) (NEAR((a),(o),(b)) ? (a): (b)+(j)) 91 | #define OVERLAP(a,b,c,d) (((a)==(c) && (b)==(d)) || MIN((a)+(b), (c)+(d)) - MAX((a), (c)) > 0) 92 | #define INTERSECT(x,y,w,h,x1,y1,w1,h1) (OVERLAP((x),(w),(x1),(w1)) && OVERLAP((y),(h),(y1),(h1))) 93 | 94 | #define WINLIST 32 95 | #define MINWINDOW 16 96 | #define UNDO 10 97 | #define TOPLEFT 1 98 | #define TOPRIGHT 2 99 | #define BOTTOMLEFT 3 100 | #define BOTTOMRIGHT 4 101 | #define CENTERLEFT 5 102 | #define CENTERRIGHT 6 103 | #define CENTERTOP 7 104 | #define CENTERBOTTOM 8 105 | #define HORIZONTAL 1 106 | #define VERTICAL 2 107 | #define SNAPLEFT 1 108 | #define SNAPRIGHT 2 109 | #define SNAPUP 3 110 | #define SNAPDOWN 4 111 | #define FOCUSLEFT 1 112 | #define FOCUSRIGHT 2 113 | #define FOCUSUP 3 114 | #define FOCUSDOWN 4 115 | #define SWAPLEFT 1 116 | #define SWAPRIGHT 2 117 | #define SWAPUP 3 118 | #define SWAPDOWN 4 119 | #define CLIENTSTATE 7 120 | 121 | // client_moveresize() flags 122 | #define MR_SMART 1<<1 123 | #define MR_SNAP 1<<2 124 | #define MR_SNAPWH 1<<3 125 | #define MR_UNCONSTRAIN 1<<4 126 | 127 | #define TAG1 1 128 | #define TAG2 (1<<1) 129 | #define TAG3 (1<<2) 130 | #define TAG4 (1<<3) 131 | #define TAG5 (1<<4) 132 | #define TAG6 (1<<5) 133 | #define TAG7 (1<<6) 134 | #define TAG8 (1<<7) 135 | #define TAG9 (1<<8) 136 | #define TAGS 9 137 | 138 | // these must be above tag bits 139 | #define RULE_IGNORE 1<<9 140 | #define RULE_FULLSCREEN 1<<10 141 | #define RULE_ABOVE 1<<11 142 | #define RULE_STICKY 1<<12 143 | #define RULE_BELOW 1<<13 144 | #define RULE_MAXHORZ 1<<14 145 | #define RULE_MAXVERT 1<<15 146 | #define RULE_TOP 1<<16 147 | #define RULE_BOTTOM 1<<17 148 | #define RULE_LEFT 1<<18 149 | #define RULE_RIGHT 1<<19 150 | #define RULE_SMALL 1<<20 151 | #define RULE_MEDIUM 1<<21 152 | #define RULE_LARGE 1<<22 153 | #define RULE_COVER 1<<23 154 | #define RULE_STEAL 1<<24 155 | #define RULE_BLOCK 1<<25 156 | #define RULE_HLOCK 1<<26 157 | #define RULE_VLOCK 1<<27 158 | #define RULE_EXPAND 1<<28 159 | #define RULE_CONTRACT 1<<29 160 | #define RULE_SKIPTBAR 1<<30 161 | #define RULE_SKIPPAGE 1LL<<31 162 | #define RULE_RAISE 1LL<<32 163 | #define RULE_LOWER 1LL<<33 164 | #define RULE_SNAPLEFT 1LL<<34 165 | #define RULE_SNAPRIGHT 1LL<<35 166 | #define RULE_SNAPUP 1LL<<36 167 | #define RULE_SNAPDOWN 1LL<<37 168 | #define RULE_SIZE 1LL<<38 169 | #define RULE_DUPLICATE 1LL<<39 170 | #define RULE_MINIMIZE 1LL<<40 171 | #define RULE_RESTORE 1LL<<41 172 | #define RULE_MONITOR1 1LL<<42 173 | #define RULE_MONITOR2 1LL<<43 174 | #define RULE_MONITOR3 1LL<<44 175 | #define RULE_ONCE 1LL<<45 176 | #define RULE_HTILE 1LL<<46 177 | #define RULE_HUNTILE 1LL<<47 178 | #define RULE_VTILE 1LL<<48 179 | #define RULE_VUNTILE 1LL<<49 180 | #define RULE_RESET 1LL<<50 181 | #define RULE_AUTOMINI 1LL<<51 182 | #define RULE_REPLACE 1LL<<52 183 | #define RULE_CENTER 1LL<<53 184 | #define RULE_POINTER 1LL<<54 185 | #define RULE_AUTOLOWER 1LL<<55 186 | 187 | #define RULESDEF 0 188 | #define RULESRESET 1 189 | 190 | // just defaults, mostly configurable from command line 191 | #define BORDER 2 192 | #define FOCUS "Royal Blue" 193 | #define BLUR "Dark Gray" 194 | #define ATTENTION "Red" 195 | #define FLASHON "Dark Green" 196 | #define FLASHOFF "Dark Red" 197 | #define SWITCHER NULL 198 | #define LAUNCHER "dmenu_run" 199 | #define FLASHPX 10 200 | #define FLASHMS 500 201 | #define FLASHMSTITLE 2000 202 | #define FLASHTITLE 1 203 | #define FLASHTITLEDEF 0 204 | #define SAYMS 2000 205 | #define MODKEY Mod4Mask 206 | #define MENUXFTFONT "mono-14" 207 | #define MENUWIDTH 50 208 | #define MENULINES 25 209 | #define MENUFG "#222222" 210 | #define MENUBG "#f2f1f0" 211 | #define MENUBGALT "#e9e8e7" 212 | #define MENUHLFG "#ffffff" 213 | #define MENUHLBG "#005577" 214 | #define MENUBC "#c0c0c0" 215 | #define TITLEXFTFONT "sans-14" 216 | #define TITLEFG "#222222" 217 | #define TITLEBG "#f2f1f0" 218 | #define TITLEBC "#c0c0c0" 219 | #define TITLEBAR "off" 220 | #define TITLEBARXFTFONT "sans-10" 221 | #define TITLEBARFOCUS "#eeeeee" 222 | #define TITLEBARBLUR "#222222" 223 | #define CONFIGFILE ".goomwwmrc" 224 | #define FOCUSCLICK 1 225 | #define FOCUSSLOPPY 2 226 | #define FOCUSSLOPPYTAG 3 227 | #define RAISE 1 228 | #define RAISEDEF 0 229 | #define WARP 1 230 | #define WARPDEF 0 231 | #define RAISEFOCUS 1 232 | #define RAISECLICK 2 233 | #define MAPSTEAL 1 234 | #define MAPBLOCK 2 235 | #define WARPFOLLOW 2 236 | #define WARPFOCUS 1 237 | #define WARPNEVER 0 238 | #define PLACEANY 1 239 | #define PLACECENTER 2 240 | #define PLACEPOINTER 3 241 | #define FLASH 1 242 | #define NOFLASH 0 243 | #define MENURETURN 1 244 | #define MENUMODUP 2 245 | #define PREFIX 1 246 | #define NOPREFIX 0 247 | #define RESIZEINC 1 248 | #define NORESIZEINC 0 249 | #define SMARTRESIZEINC 2 250 | #define LARGELEFT 1 251 | #define LARGERIGHT 2 252 | #define TILENONE 0 253 | #define TILESMART 1 254 | 255 | #define SMARTRESIZEINC_IGNORE "^(xterm|urxvt)$" 256 | 257 | #define winlist_ascend(l,i,w) for ((i) = 0; (i) < (l)->len && (((w) = (l)->array[i]) || 1); (i)++) 258 | #define winlist_descend(l,i,w) for ((i) = (l)->len-1; (i) >= 0 && (((w) = (l)->array[i]) || 1); (i)--) 259 | 260 | #define clients_ascend(l,i,w,c) winlist_ascend(l,i,w) if (((c) = client_create(w))) 261 | #define clients_descend(l,i,w,c) winlist_descend(l,i,w) if (((c) = client_create(w))) 262 | 263 | #define managed_ascend(i,w,c) clients_ascend(windows_in_play(),i,w,c) if ((c)->manage && (c)->visible) 264 | #define managed_descend(i,w,c) clients_descend(windows_in_play(),i,w,c) if ((c)->manage && (c)->visible) 265 | 266 | #define tag_ascend(i,w,c,t) managed_ascend(i, w, c) if (!(t) || (c)->cache->tags & (t)) 267 | #define tag_descend(i,w,c,t) managed_descend(i, w, c) if (!(t) || (c)->cache->tags & (t)) 268 | 269 | // window lists 270 | typedef struct { 271 | Window *array; // actual window ids 272 | void **data; // an associated struct 273 | short len; 274 | } winlist; 275 | 276 | // usable space on a monitor 277 | typedef struct { 278 | short x, y, w, h, l, r, t, b; 279 | } workarea; 280 | 281 | // snapshot a window's size/pos and EWMH state 282 | typedef struct _winundo { 283 | short x, y, w, h, states; 284 | Atom state[CLIENTSTATE]; 285 | struct _winundo *next; 286 | } winundo; 287 | 288 | // track general window stuff 289 | // every window we know about gets one of these, even if it's empty 290 | typedef struct { 291 | bool have_closed; // true when we've previously politely sent a close request 292 | bool last_corner; // the last screen corner, used to make corner seem sticky during resizing 293 | bool hlock, vlock; // horizontal and vertical size/position locks 294 | bool has_mapped; // true when a client has mapped previously. used to avoid applying rules 295 | unsigned int tags; // desktop tags 296 | winundo *ewmh; // undo size/pos for EWMH FULLSCREEN/MAXIMIZE_HORZ/MAXIMIZE_VERT toggles 297 | winundo *undo; // general size/pos undo LIFO linked list 298 | box *frame; // titlebar & border, but NOT reparented! 299 | textbox *title; 300 | bool is_ours; // set for any windows goomwwm creates 301 | Window app; 302 | } wincache; 303 | 304 | // rule for controlling window size/pos/behaviour 305 | typedef struct _rule { 306 | char *pattern; // POSIX regex pattern to match on class/name/title 307 | regex_t re; // precompiled regex 308 | bitmap flags; // RULE_* flags 309 | short w, h; // manually specified width/height 310 | bool w_is_pct, h_is_pct; // true if w/h is a percentage of screen size 311 | struct _rule *next; 312 | } winrule; 313 | 314 | // all global rules. this is separate from rule sets! 315 | winrule *config_rules = NULL; 316 | 317 | // a set of rules to execute in order, like a mini script. 318 | // this is separate from global rules! 319 | typedef struct _ruleset { 320 | char *name; // any name, for disply in the popup menu 321 | winrule *rules; // linked list of rules in reverse-definition order 322 | struct _ruleset *next; 323 | } winruleset; 324 | 325 | // all defined rulesets 326 | winruleset *config_rulesets = NULL; 327 | 328 | // for converting rule strings to bit flags 329 | typedef struct { 330 | const char *name; 331 | bitmap flag; 332 | } winrulemap; 333 | 334 | winrulemap rulemap[] = { 335 | { "tag1", TAG1 }, 336 | { "tag2", TAG2 }, 337 | { "tag3", TAG3 }, 338 | { "tag4", TAG4 }, 339 | { "tag5", TAG5 }, 340 | { "tag6", TAG6 }, 341 | { "tag7", TAG7 }, 342 | { "tag8", TAG8 }, 343 | { "tag9", TAG9 }, 344 | { "ignore", RULE_IGNORE }, 345 | { "above", RULE_ABOVE }, 346 | { "sticky", RULE_STICKY }, 347 | { "below", RULE_BELOW }, 348 | { "fullscreen", RULE_FULLSCREEN }, 349 | { "maximize_horz", RULE_MAXHORZ }, 350 | { "maximize_vert", RULE_MAXVERT }, 351 | { "top", RULE_TOP }, 352 | { "bottom", RULE_BOTTOM }, 353 | { "left", RULE_LEFT }, 354 | { "right", RULE_RIGHT }, 355 | { "center", RULE_CENTER }, 356 | { "pointer", RULE_POINTER }, 357 | { "small", RULE_SMALL }, 358 | { "medium", RULE_MEDIUM }, 359 | { "large", RULE_LARGE }, 360 | { "cover", RULE_COVER }, 361 | { "replace", RULE_REPLACE }, 362 | { "steal", RULE_STEAL }, 363 | { "block", RULE_BLOCK }, 364 | { "hlock", RULE_HLOCK }, 365 | { "vlock", RULE_VLOCK }, 366 | { "expand", RULE_EXPAND }, 367 | { "contract", RULE_CONTRACT }, 368 | { "skip_taskbar", RULE_SKIPTBAR }, 369 | { "skip_pager", RULE_SKIPPAGE }, 370 | { "raise", RULE_RAISE }, 371 | { "lower", RULE_LOWER }, 372 | { "snap_left", RULE_SNAPLEFT }, 373 | { "snap_right", RULE_SNAPRIGHT }, 374 | { "snap_up", RULE_SNAPUP }, 375 | { "snap_down", RULE_SNAPDOWN }, 376 | { "duplicate", RULE_DUPLICATE }, 377 | { "minimize", RULE_MINIMIZE }, 378 | { "restore", RULE_RESTORE }, 379 | { "monitor1", RULE_MONITOR1 }, 380 | { "monitor2", RULE_MONITOR2 }, 381 | { "monitor3", RULE_MONITOR3 }, 382 | { "once", RULE_ONCE }, 383 | { "htile", RULE_HTILE }, 384 | { "vtile", RULE_VTILE }, 385 | { "huntile", RULE_HUNTILE }, 386 | { "vuntile", RULE_VUNTILE }, 387 | { "reset", RULE_RESET }, 388 | { "minimize_auto", RULE_AUTOMINI }, 389 | { "lower_auto", RULE_AUTOLOWER }, 390 | }; 391 | 392 | // a placeholder 393 | char *empty = ""; 394 | 395 | // collect and store data on a window 396 | typedef struct { 397 | Window window; // window's id 398 | Window trans; // our transient_for 399 | XWindowAttributes xattr; // copy of cache_xattr data 400 | XSizeHints xsize; // only loaded after client_extended_data() 401 | short x, y, w, h; // size/pos pulled from xattr + borders 402 | short states; // number of EWMH states set 403 | short initial_state; // pulled from wm hints 404 | short border_width; // pulled from xwindowattributes 405 | short titlebar_height; 406 | // general flags 407 | bool manage, visible, input, focus, active, minimized, shaded, decorate, urgent; 408 | bool is_full, is_left, is_top, is_right, is_bottom, is_xcenter, is_ycenter; 409 | bool is_maxh, is_maxv, is_described, is_extended, is_ruled; 410 | // descriptive buffers loaded after client_descriptive_data() 411 | char *title, *class, *name; 412 | // EWMH states and type 413 | Atom state[CLIENTSTATE], type; 414 | workarea monitor; // monitor holding the window, with strut padding detected 415 | wincache *cache; // a persistent cache for this window (clients are freed each event) 416 | winrule *rule; // loaded after client_rule 417 | } client; 418 | 419 | // built-in filterable popup menu list 420 | struct localmenu { 421 | Window window; 422 | GC gc; 423 | Pixmap canvas; 424 | XftFont *font; 425 | XftColor *color; 426 | XftDraw *draw; 427 | XftColor fg, bg, hlfg, hlbg, bgalt; 428 | unsigned long xbg; 429 | char **lines, **filtered; 430 | short done, max_lines, num_lines, input_size, line_height; 431 | short current, width, height, horz_pad, vert_pad, offset; 432 | char *input, *selected, *prompt; 433 | XIM xim; 434 | XIC xic; 435 | }; 436 | 437 | 438 | // config settings 439 | unsigned int config_modkey, config_prefix_mode, 440 | config_warp_mode, config_flash_title, 441 | config_border_width, config_flash_width, config_flash_ms, 442 | config_map_mode, config_menu_select, config_menu_width, 443 | config_menu_lines, config_focus_mode, config_raise_mode, 444 | config_window_placement, config_only_auto, config_resize_inc, 445 | config_tile_mode, config_titlebar_height; 446 | 447 | char *config_border_focus, *config_border_blur, *config_border_attention, 448 | *config_menu_font, *config_menu_fg, *config_menu_bg, 449 | *config_menu_hlfg, *config_menu_hlbg, *config_menu_bgalt, 450 | *config_title_font, *config_title_fg, *config_title_bg, *config_title_bc, 451 | *config_titlebar_font, *config_titlebar_focus, *config_titlebar_blur, 452 | *config_menu_bc, *config_resizeinc_ignore, 453 | *config_flash_on, *config_flash_off; 454 | 455 | char *config_switcher, *config_launcher, *config_apps_patterns[10]; 456 | // these must be the same size and keys must remain in ascending order (regardless of key value/position order) 457 | KeySym config_apps_keysyms[] = { XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, XK_0, 0 }; 458 | KeySym config_tags_keysyms[] = { XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, XK_F9, 0, 0 }; 459 | 460 | #define MAXMODCODES 16 461 | unsigned int config_modkeycodes[MAXMODCODES+1]; 462 | 463 | #define KEY_ENUM(a,b,c,d) a 464 | #define KEY_KSYM(a,b,c,d) [a] = c 465 | #define KEY_KMOD(a,b,c,d) [a] = b 466 | #define KEY_CARG(a,b,c,d) #d 467 | 468 | // default keybindings 469 | #define KEYLIST(X) \ 470 | X(KEY_RIGHT, 0, XK_Right, -right ),\ 471 | X(KEY_LEFT, 0, XK_Left, -left ),\ 472 | X(KEY_UP, 0, XK_Up, -up ),\ 473 | X(KEY_DOWN, 0, XK_Down, -down ),\ 474 | X(KEY_SNAPRIGHT, ShiftMask, XK_Right, -snapright ),\ 475 | X(KEY_SNAPLEFT, ShiftMask, XK_Left, -snapleft ),\ 476 | X(KEY_SNAPUP, ShiftMask, XK_Up, -snapup ),\ 477 | X(KEY_SNAPDOWN, ShiftMask, XK_Down, -snapdown ),\ 478 | X(KEY_FOCUSRIGHT, 0, XK_l, -focusright),\ 479 | X(KEY_FOCUSLEFT, 0, XK_j, -focusleft ),\ 480 | X(KEY_FOCUSUP, 0, XK_i, -focusup ),\ 481 | X(KEY_FOCUSDOWN, 0, XK_k, -focusdown ),\ 482 | X(KEY_SWAPRIGHT, ShiftMask, XK_l, -swapright ),\ 483 | X(KEY_SWAPLEFT, ShiftMask, XK_j, -swapleft ),\ 484 | X(KEY_SWAPUP, ShiftMask, XK_i, -swapup ),\ 485 | X(KEY_SWAPDOWN, ShiftMask, XK_k, -swapdown ),\ 486 | X(KEY_SHRINK, 0, XK_Page_Down, -shrink ),\ 487 | X(KEY_GROW, 0, XK_Page_Up, -grow ),\ 488 | X(KEY_DEC, ShiftMask, XK_Page_Down, -decrease ),\ 489 | X(KEY_INC, ShiftMask, XK_Page_Up, -increase ),\ 490 | X(KEY_FULLSCREEN, 0, XK_f, -fullscreen),\ 491 | X(KEY_ABOVE, 0, XK_a, -above ),\ 492 | X(KEY_BELOW, 0, XK_b, -below ),\ 493 | X(KEY_STICKY, 0, XK_s, -sticky ),\ 494 | X(KEY_VMAX, 0, XK_Home, -vmax ),\ 495 | X(KEY_HMAX, 0, XK_End, -hmax ),\ 496 | X(KEY_EXPAND, 0, XK_Return, -expand ),\ 497 | X(KEY_CONTRACT, 0, XK_BackSpace, -contract ),\ 498 | X(KEY_VLOCK, 0, XK_Insert, -vlock ),\ 499 | X(KEY_HLOCK, 0, XK_Delete, -hlock ),\ 500 | X(KEY_TAG, 0, XK_t, -tag ),\ 501 | X(KEY_SWITCH, 0, XK_Tab, -switch ),\ 502 | X(KEY_TSWITCH, 0, XK_grave, -tswitch ),\ 503 | X(KEY_CYCLE, 0, XK_c, -cycle ),\ 504 | X(KEY_CLOSE, 0, XK_Escape, -close ),\ 505 | X(KEY_TAGCLOSE, ShiftMask, XK_Escape, -tagclose ),\ 506 | X(KEY_HTILE, 0, XK_h, -htile ),\ 507 | X(KEY_VTILE, 0, XK_v, -vtile ),\ 508 | X(KEY_HUNTILE, ShiftMask, XK_h, -huntile ),\ 509 | X(KEY_VUNTILE, ShiftMask, XK_v, -vuntile ),\ 510 | X(KEY_UNDO, 0, XK_u, -undo ),\ 511 | X(KEY_TAGNEXT, 0, XK_m, -tagnext ),\ 512 | X(KEY_TAGPREV, 0, XK_n, -tagprev ),\ 513 | X(KEY_DUPLICATE, 0, XK_d, -duplicate ),\ 514 | X(KEY_INFO, 0, XK_w, -info ),\ 515 | X(KEY_QUIT, 0, XK_Pause, -quit ),\ 516 | X(KEY_PREFIX, 0, XK_VoidSymbol, -prefix ),\ 517 | X(KEY_MINIMIZE, 0, XK_slash, -minimize ),\ 518 | X(KEY_RULE, 0, XK_comma, -runrule ),\ 519 | X(KEY_RULESET, 0, XK_period, -runruleset),\ 520 | X(KEY_TAGONLY, 0, XK_o, -only ),\ 521 | X(KEY_COMMAND, 0, XK_F12, -command ),\ 522 | X(KEY_LARGELEFT, 0, XK_bracketleft, -largeleft ),\ 523 | X(KEY_LARGERIGHT, 0, XK_bracketright, -largeright),\ 524 | X(KEY_LAUNCH, 0, XK_x, -launch ) 525 | 526 | enum { KEYLIST(KEY_ENUM) }; 527 | KeySym keymap[] = { KEYLIST(KEY_KSYM), 0 }; 528 | unsigned int keymodmap[] = { KEYLIST(KEY_KMOD), 0 }; 529 | char *keyargs[] = { KEYLIST(KEY_CARG), NULL }; 530 | 531 | unsigned int NumlockMask = 0; 532 | Display *display; Screen *screen; Window root; int screen_id; 533 | Time latest = CurrentTime; 534 | 535 | // mouse move/resize controls 536 | // see ButtonPress,MotionNotify 537 | 538 | struct mouse_drag { 539 | XButtonEvent button; 540 | XWindowAttributes attr; 541 | box *overlay; 542 | short x, y, w, h; 543 | unsigned int flags; 544 | }; 545 | struct mouse_drag *mouse_dragger = NULL; 546 | 547 | bool quit_pressed_once = 0; 548 | bool prefix_mode_active = 0; 549 | Cursor prefix_cursor; 550 | Window supporting; 551 | 552 | // tracking windows 553 | winlist *windows, *windows_activated, *windows_minimized, *windows_shaded; 554 | unsigned int current_tag = TAG1; 555 | 556 | // caches used to reduce X server round trips 557 | winlist *cache_client; 558 | winlist *cache_xattr; 559 | winlist *cache_inplay; 560 | 561 | workarea cache_monitor[6]; 562 | 563 | static int (*xerror)(Display *, XErrorEvent *); 564 | 565 | typedef struct { 566 | unsigned long flags, functions, decorations; 567 | } motif_hints; 568 | 569 | #define ATOM_ENUM(x) x 570 | #define ATOM_CHAR(x) #x 571 | 572 | #define GENERAL_ATOMS(X) \ 573 | X(_MOTIF_WM_HINTS),\ 574 | X(WM_DELETE_WINDOW),\ 575 | X(WM_STATE),\ 576 | X(WM_TAKE_FOCUS),\ 577 | X(WM_NAME),\ 578 | X(WM_CLASS),\ 579 | X(WM_WINDOW_ROLE),\ 580 | X(WM_PROTOCOLS) 581 | 582 | enum { GENERAL_ATOMS(ATOM_ENUM), ATOMS }; 583 | const char *atom_names[] = { GENERAL_ATOMS(ATOM_CHAR) }; 584 | Atom atoms[ATOMS]; 585 | 586 | #define EWMH_ATOMS(X) \ 587 | X(_NET_SUPPORTING_WM_CHECK),\ 588 | X(_NET_CLIENT_LIST),\ 589 | X(_NET_CLIENT_LIST_STACKING),\ 590 | X(_NET_NUMBER_OF_DESKTOPS),\ 591 | X(_NET_CURRENT_DESKTOP),\ 592 | X(_NET_DESKTOP_GEOMETRY),\ 593 | X(_NET_DESKTOP_VIEWPORT),\ 594 | X(_NET_WORKAREA),\ 595 | X(_NET_ACTIVE_WINDOW),\ 596 | X(_NET_CLOSE_WINDOW),\ 597 | X(_NET_MOVERESIZE_WINDOW),\ 598 | X(_NET_WM_NAME),\ 599 | X(_NET_WM_PID),\ 600 | X(_NET_WM_WINDOW_TYPE),\ 601 | X(_NET_WM_WINDOW_TYPE_DESKTOP),\ 602 | X(_NET_WM_WINDOW_TYPE_DOCK),\ 603 | X(_NET_WM_WINDOW_TYPE_SPLASH),\ 604 | X(_NET_WM_WINDOW_TYPE_UTILITY),\ 605 | X(_NET_WM_WINDOW_TYPE_TOOLBAR),\ 606 | X(_NET_WM_WINDOW_TYPE_MENU),\ 607 | X(_NET_WM_WINDOW_TYPE_DIALOG),\ 608 | X(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU),\ 609 | X(_NET_WM_WINDOW_TYPE_POPUP_MENU),\ 610 | X(_NET_WM_WINDOW_TYPE_TOOLTIP),\ 611 | X(_NET_WM_WINDOW_TYPE_NOTIFICATION),\ 612 | X(_NET_WM_WINDOW_TYPE_COMBO),\ 613 | X(_NET_WM_WINDOW_TYPE_NORMAL),\ 614 | X(_NET_WM_STATE),\ 615 | X(_NET_WM_STATE_MODAL),\ 616 | X(_NET_WM_STATE_STICKY),\ 617 | X(_NET_WM_STATE_MAXIMIZED_VERT),\ 618 | X(_NET_WM_STATE_MAXIMIZED_HORZ),\ 619 | X(_NET_WM_STATE_SHADED),\ 620 | X(_NET_WM_STATE_SKIP_TASKBAR),\ 621 | X(_NET_WM_STATE_SKIP_PAGER),\ 622 | X(_NET_WM_STATE_HIDDEN),\ 623 | X(_NET_WM_STATE_FULLSCREEN),\ 624 | X(_NET_WM_STATE_ABOVE),\ 625 | X(_NET_WM_STATE_BELOW),\ 626 | X(_NET_WM_STATE_DEMANDS_ATTENTION),\ 627 | X(_NET_WM_STATE_ADD),\ 628 | X(_NET_WM_STATE_REMOVE),\ 629 | X(_NET_WM_STATE_TOGGLE),\ 630 | X(_NET_WM_ALLOWED_ACTIONS),\ 631 | X(_NET_WM_ACTION_MOVE),\ 632 | X(_NET_WM_ACTION_RESIZE),\ 633 | X(_NET_WM_ACTION_MINIMIZE),\ 634 | X(_NET_WM_ACTION_SHADE),\ 635 | X(_NET_WM_ACTION_STICK),\ 636 | X(_NET_WM_ACTION_MAXIMIZE_VERT),\ 637 | X(_NET_WM_ACTION_MAXIMIZE_HORZ),\ 638 | X(_NET_WM_ACTION_FULLSCREEN),\ 639 | X(_NET_WM_ACTION_CHANGE_DESKTOP),\ 640 | X(_NET_WM_ACTION_CLOSE),\ 641 | X(_NET_WM_STRUT),\ 642 | X(_NET_WM_STRUT_PARTIAL),\ 643 | X(_NET_FRAME_EXTENTS),\ 644 | X(_NET_REQUEST_FRAME_EXTENTS),\ 645 | X(_NET_WM_DESKTOP),\ 646 | X(_NET_WM_WINDOW_OPACITY),\ 647 | X(_NET_SUPPORTED) 648 | 649 | enum { EWMH_ATOMS(ATOM_ENUM), NETATOMS }; 650 | const char *netatom_names[] = { EWMH_ATOMS(ATOM_CHAR) }; 651 | Atom netatoms[NETATOMS]; 652 | 653 | #define ADD 1 654 | #define REMOVE 0 655 | #define TOGGLE 2 656 | 657 | #define GOOMWWM_ATOMS(X) \ 658 | X(GOOMWWM_LOG),\ 659 | X(GOOMWWM_MESSAGE),\ 660 | X(GOOMWWM_QUIT),\ 661 | X(GOOMWWM_RULESET),\ 662 | X(GOOMWWM_RULE),\ 663 | X(GOOMWWM_NOTICE),\ 664 | X(GOOMWWM_FIND_OR_START),\ 665 | X(GOOMWWM_RESTART) 666 | 667 | enum { GOOMWWM_ATOMS(ATOM_ENUM), GATOMS }; 668 | const char *gatom_names[] = { GOOMWWM_ATOMS(ATOM_CHAR) }; 669 | Atom gatoms[GATOMS]; 670 | -------------------------------------------------------------------------------- /goomwwm.md: -------------------------------------------------------------------------------- 1 | % GOOMWWM(1) 2 | 3 | # NAME 4 | 5 | goomwwm \- Get out of my way, Window Manager! 6 | 7 | # SYNOPSIS 8 | 9 | **goomwwm** [ options ] 10 | 11 | # DESCRIPTION 12 | 13 | **goomwwm** is an X11 window manager implemented in C as a cleanroom software project. It manages windows in a minimal floating layout with normal mouse controls, while also providing flexible keyboard-driven controls for window switching, sizing, moving, tagging, and tiling. It is fast, lightweight, modeless, Xinerama-aware, and EWMH compatible wherever possible. 14 | 15 | Keyboard window movement occurs on a 3x3 fullscreen grid. For example, a top-left aligned window moved to the right would reappear top-center, and if moved again, top-right. 16 | 17 | Keyboard window sizing moves windows through four basic sizes that tile well: 1/9th of screen (1/3 width, 1/3 height), 1/4th, 4/9th, and fullscreen. Combined with EWMH horizontal and vertical maxmimization, plus some snap-to-edge and expand-to-fill-space controls, manual tiling is easy. 18 | 19 | Windows are grouped by tags. Activating a tag raises all windows in the tag. A window may have multiple tags. EWMH panels, pagers, and taskbars see tags as desktops. 20 | 21 | License: MIT/X11 22 | 23 | # USAGE 24 | 25 | See options below for custom key combinations. These are the defaults. 26 | 27 | Below, **Mod** refers to the global modifier key which is **Mod4** by default. Usually Mod4 is mapped to the left Windows key. To change it, see **-modkey**. 28 | 29 | Mod-Button1 30 | : (Mouse!) Move a window. 31 | 32 | Mod-Button3 33 | : (Mouse!) Resize a window. 34 | 35 | Mod-[F1-F9] 36 | : Set the current tag and raise all its windows. A window may be in multiple tags. For EWMH panel and pager integration tags roughly simulate desktops (always 9), but there will be differences in behavior. 37 | 38 | Mod-Shift-[F1-F9] 39 | : Toggle active window's tags without switching current tag. 40 | 41 | Mod-[1-9] 42 | : Do a case-insensitive keyword search for window by WM_CLASS and title. If found, raise and focus. If not found, try to execute the keyword and start the app. See **-1** through **-9** args. 43 | 44 | Mod-Shift-[1-9] 45 | : Force execution of keyword used in **Mod-[1-9]** even if a copy of the window already exists. 46 | 47 | Mod-Tab 48 | : Switch between all windows by popup menu. This is partly based on **dmenu** but more tightly integrated with the window manager and centered on screen. Type text to filter the menu entries. Navigate with the arrow keys. Return to select. 49 | 50 | Mod-` (Grave/Backtick) 51 | : Switch between all windows in current tag by popup menu. 52 | 53 | Mod-c 54 | : Cycle through windows in the same tag and position as the active window. 55 | 56 | Mod-Escape 57 | : Close the active window. 58 | 59 | Mod-PageUp 60 | : Grow the active window through four basic sizes that tile well together: 1/9th, 1/4th, 4/9th, or fullscreen. 61 | 62 | Mod-PageDown 63 | : Shrink the active window through the same four sizes. 64 | 65 | Mod-Shift-PageUp 66 | : Increase size of the active window. 67 | 68 | Mod-Shift-PageDown 69 | : Decrease size of the active window. 70 | 71 | Mod-h 72 | : Horizontally tile the active window and others with the same tag, position, and size. 73 | 74 | Mod-v 75 | : Vertically tile the active window and others with the same tag, position, and size. 76 | 77 | Mod-Shift-h 78 | : Opposite of Mod-h. Horizontally un-tile. 79 | 80 | Mod-Shift-v 81 | : Opposite of Mod-v. Vertically un-tile. 82 | 83 | Mod-x 84 | : Run the launcher (by default: dmenu_run). 85 | 86 | Mod-a 87 | : Toggle _NET_WM_STATE_ABOVE for the active window. Corners will flash to acknowledge. 88 | 89 | Mod-b 90 | : Toggle _NET_WM_STATE_BELOW for the active window. Corners will flash to acknowledge. 91 | 92 | Mod-f 93 | : Toggle _NET_WM_STATE_FULLSCREEN for the active window. Corners will flash to acknowledge. 94 | 95 | Mod-d 96 | : Resize active window to match the window immediately underneath, in the same tag. 97 | 98 | Mod-Home 99 | : Toggle _NET_WM_STATE_MAXIMIXED_HORZ for the active window. Corners will flash to acknowledge. 100 | 101 | Mod-End 102 | : Toggle _NET_WM_STATE_MAXIMIXED_VERT for the active window. Corners will flash to acknowledge. 103 | 104 | Mod-Return 105 | : Expand active window to fill surrounding space without obscuring any fully visible window. 106 | 107 | Mod-Backspace 108 | : Contract active window to fill an underlying space without obscuring window that would then be fully visible. 109 | 110 | Mod-Insert 111 | : Toggle vertical move/resize lock for the active window. 112 | 113 | Mod-Delete 114 | : Toggle horizontal move/resize lock for the active window. 115 | 116 | Mod-Left 117 | : Move the active window left within a 3x3 grid. 118 | 119 | Mod-Right 120 | : Move the active window right within a 3x3 grid. 121 | 122 | Mod-Up 123 | : Move the active window up within a 3x3 grid. 124 | 125 | Mod-Down 126 | : Move the active window down within a 3x3 grid. 127 | 128 | Mod-Shift-Left (Left/Right/Up/Down) 129 | : Snap the active window to the nearest border, by direction. 130 | 131 | Mod-u 132 | : Undo the last size/position change for the active window. Undo is 10 levels deep. 133 | 134 | Mod-i 135 | : Switch focus upward from the active window. 136 | 137 | Mod-j 138 | : Switch focus to the left of the active window. 139 | 140 | Mod-k 141 | : Switch focus downward from the active window. 142 | 143 | Mod-l 144 | : Switch focus to the right of the active window. 145 | 146 | Mod-Shift-i (i/j/k/l) 147 | : Swap the active window position with another window by direction. 148 | 149 | Mod-t 150 | : Toggle the active window's membership of the current tag. 151 | 152 | Mod-m 153 | : Cycle tag forward. 154 | 155 | Mod-n 156 | : Cycle tag backward. 157 | 158 | Mod-w 159 | : Display active window title. 160 | 161 | Mod-, (comma) 162 | : Reapply active window rule. 163 | 164 | Mod-. (period) 165 | : Switch between defined rule sets. 166 | 167 | Mod-/ (slash) 168 | : Minimize a window. 169 | 170 | Mod-[ (left square brancket) 171 | : Move and resize a window to cover the left 2/3 of a monitor. 172 | 173 | Mod-] (right square brancket) 174 | : Move and resize a window to cover the right 2/3 of a monitor. 175 | 176 | Mod-o 177 | : Show only windows in the current tag. Hide everything else. 178 | 179 | Mod-Pause (press twice) 180 | : Quit goomwwm. 181 | 182 | # OPTIONS 183 | 184 | All key combinations use the same global modifier key by default, which is **Mod4** (usually Win/Meta). If the default modifier is changed with **-modkey** then all key combinations that do not specify their own custom modifiers will change to use the new modifier automatically. 185 | 186 | All options below that set a custom key therefore implicitly combine it with the default modifier key. For example, the following both mean **Mod4-a**: 187 | 188 | goomwwm -above a 189 | goomwwm -above mod4-a 190 | 191 | Any combiation of **shift**, **control**, **mod1** (usually Alt), **mod2**, **mod3**, **mod4** (usually Win/Meta), **mod5** (sometimes AltGr) may be supplied for any key combination: 192 | 193 | goomwwm -above control-shift-a 194 | 195 | To explicitly bind a key without any modifier, not even the default, use **nomod**: 196 | 197 | goomwwm -above nomod-f12 198 | 199 | Note that this would capture F12 globally, making it unusable for anything else. Use *nomod* with care. 200 | 201 | -1 -2 -3 -4 -5 -6 -7 -8 -9 202 | : Set a number key to a keyword to search for a window by WM_CLASS, application name, or title, and then raise and focus it. If a window is not found, the string supplied will be executed as a shell command to start the application. 203 | 204 | goomwwm -1 chromium -2 firefox -3 xterm 205 | 206 | Above, Mod-1 would match the top-most Chromium window. 207 | 208 | Many applications politely set their WM_CLASS to a sane value (eg, Chromium uses Chromium, xterm uses XTerm) or append their name to their window titles, which nicely matches their binary names if we use case-insensitive string comparison. This allows us to use the one string to both search and start. 209 | 210 | Applications that are not so friendly can sometimes be wrapped in a shell script of the appropriate name in your $PATH somewhere. 211 | 212 | Sometimes it can be useful to limit the match to WM_CLASS or name (eg, when a browser window title includes another app's name causing a false positive). Simply use **class:** or **name:** prefixes: 213 | 214 | goomwwm -1 class:chromium 215 | 216 | -above 217 | : Set an X11 key name to toggle _NET_WM_STATE_ABOVE for the active window (default: XK_a). 218 | 219 | goomwwm -above a 220 | 221 | -appkeys 222 | : Specify the keys to use for app search-or-launch as set by **-1** through **-9** (default: numbers). 223 | 224 | Valid settings are: 225 | 226 | numbers 227 | : Use the number keys 1-9 228 | 229 | functions 230 | : Use the function keys F1-F9 231 | 232 | If function keys are used for app launchers the number keys will be used to swap tags, and vice versa. 233 | 234 | -attention 235 | : Set the border color (X11 named color or hex #rrggbb) for an inactive window with _NET_WM_STATE_DEMANDS_ATTENTION (default: Red). 236 | 237 | goomwwm -attention Red 238 | 239 | -auto 240 | : Search for an app at startup and autostart it if not found. Uses the same WM_CLASS/name/title matching rules as the -1 through -9 arguments (default: none). 241 | 242 | goomwwm -auto chromium 243 | 244 | Above, chromium will only be started if a chromium window does not already exist. 245 | 246 | 247 | -below 248 | : Set an X11 key name to toggle _NET_WM_STATE_BELOW for the active window (default: XK_b). 249 | 250 | goomwwm -below b 251 | 252 | -blur 253 | : Set the border color (X11 named color or hex #rrggbb) for unfocused windows (default: Dark Gray). 254 | 255 | goomwwm -blur "Dark Gray" 256 | 257 | -border 258 | : Set the border width in pixels for all managed windows (default: 2). 259 | 260 | goomwwm -border 2 261 | 262 | -close 263 | : Set an X11 key name to gracefully close the active window (default: XK_Escape). 264 | 265 | goomwwm -close Escape 266 | 267 | -config 268 | : Parse extra options from a text file. 269 | 270 | goomwwm -config /path/to/config.txt 271 | 272 | The file format is any command line options without the leading hyphen. Comments and blank lines are acceptable. 273 | 274 | # a comment 275 | 2 chromium 276 | 2 konsole 277 | close Escape 278 | menufont mono-14 279 | 280 | If it exists, **$HOME/.goomwwmrc** is automatically parsed. 281 | 282 | -cycle 283 | : Set an X11 key name to cycle windows in the same tag and position as the active window (default: XK_c). 284 | 285 | goomwwm -cycle c 286 | 287 | 288 | -contract 289 | : Set an X11 key name to contract the active window to fill an underlying space without obscuring any other window in the current tag that would then be fully visible (default: XK_Contract). Opposite of -expand. 290 | 291 | goomwwm -contract BackSpace 292 | 293 | -decrease 294 | : Set an X11 key to decrementally resize the active window (default: shift+page_up). 295 | 296 | goomwwm -decrease shift+page_down 297 | 298 | -down 299 | : Set an X11 key name to move the active window downward in a 3x3 grid (default: XK_Down). 300 | 301 | goomwwm -down Down 302 | 303 | -duplicate 304 | : Set an X11 key name to resize the active window to match the window immediately underneath, in the same tag (default: XK_d). 305 | 306 | goomwwm -duplicate d 307 | 308 | -exec 309 | : Execute a command at startup but only after goomwwm has started successfully (default: none). Useful for pre-lanching apps, but also see **-auto**. 310 | 311 | goomwwm -exec firefox 312 | 313 | -expand 314 | : Set an X11 key name to expand the active window to fill adjacent space without obscuring any other fully visible window in the current tag (default: XK_Return). Opposite of -contract. 315 | 316 | goomwwm -expand Return 317 | 318 | -largeleft 319 | : Set an X11 key to move and resize the active window to cover the left 2/3 of a monitor (default: XK_bracketleft). 320 | 321 | goomwwm -largeleft bracketleft 322 | 323 | -largeright 324 | : Set an X11 key to move and resize the active window to cover the right 2/3 of a monitor (default: XK_bracketright). 325 | 326 | goomwwm -largeleft bracketleft 327 | 328 | -launch 329 | : Set an X11 key to run the application launcher (default: XK_x). 330 | 331 | goomwwm -launch x 332 | 333 | -launcher 334 | : Set a custom application launcher to execute on **Mod-x** (default: dmenu_run). 335 | 336 | goomwwm -launcher dmenu_run 337 | 338 | -flashms 339 | : Set the duration in milliseconds of the window flash indicators (default: 500). 340 | 341 | goomwwm -flashms 500 342 | 343 | -flashon 344 | : Set the color (X11 named color or hex #rrggbb) of the flash indicator when toggling _NET_WM_STATE_* on (default: Dark Green). 345 | 346 | goomwwm -flashon "Dark Green" 347 | 348 | -flashoff 349 | : Set the color (X11 named color or hex #rrggbb) of the flash indicator when toggling _NET_WM_STATE_* off (default: Dark Red). 350 | 351 | goomwwm -flashon "Dark Red" 352 | 353 | -flashpx 354 | : Set the size in pixels of window flash indicators (currently a colored square in each window corner) (default: 10). 355 | 356 | goomwwm -flashpx 10 357 | 358 | -flashtitle 359 | : Wether to flash a window's title when changing focus or other modes (default: hide). Regardless of this setting, **Mod-w** always displays a window's title. 360 | 361 | goomwwm -flashtitle hide 362 | 363 | Valid modes: 364 | 365 | show 366 | : Flash title bar centered on the window. 367 | 368 | hide 369 | : Do no flash title bar. 370 | 371 | -focus 372 | : Set the border color (X11 named color or hex #rrggbb) for the focused window (default: Royal Blue). 373 | 374 | goomwwm -focus "Royal Blue" 375 | 376 | -focusdown 377 | : Set an X11 key name to switch focus downward from the active window within the current tag (default: XK_k). 378 | 379 | goomwwm -focusdown k 380 | 381 | -focusleft 382 | : Set an X11 key name to switch focus to left of the active window within the current tag (default: XK_j). 383 | 384 | goomwwm -focusleft j 385 | 386 | -focusmode 387 | : Control the window focus mode (default: click). 388 | 389 | goomwwm -focusmode click 390 | 391 | Valid settings are: 392 | 393 | click 394 | : focus on mouse click. 395 | 396 | sloppy 397 | : focus follows mouse 398 | 399 | sloppytag 400 | : focus follows mouse within current tag. 401 | 402 | -focusright 403 | : Set an X11 key name to switch focus to right of the active window within the current tag (default: XK_l). 404 | 405 | goomwwm -focusright l 406 | 407 | -focusup 408 | : Set an X11 key name to switch focus upward form the active window within the current tag (default: XK_i). 409 | 410 | goomwwm -focusup i 411 | 412 | -fullscreen 413 | : Set an X11 key name to toggle _NET_WM_STATE_FULLSCREEN for the active window (default: XK_f). 414 | 415 | goomwwm -fullscreen f 416 | 417 | -grow 418 | : Set an X11 key name to increase the active window size (default: XK_Page_Up) through four basic sizes that tile well together: 1/9th, 1/4th, 4/9th, or fullscreen. 419 | 420 | goomwwm -grow Page_Up 421 | 422 | -hlock 423 | : Set an X11 key name to toggle horizontal move/resize lock for the active window (default: XK_Delete). 424 | 425 | goomwwm -hlock Delete 426 | 427 | -hmax 428 | : Set an X11 key name to toggle _NET_WM_STATE_MAXIMIXED_HORZ for the active window (default: XK_End). 429 | 430 | goomwwm -hmax End 431 | 432 | 433 | -htile 434 | : Set an X11 key to horizontally tile the active window and others with the same tag, position, and size (default: XK_h). 435 | 436 | goomwwm -htile h 437 | 438 | -huntile 439 | : Set an X11 key to do the opposite of -htile. 440 | 441 | goomwwm -huntile h 442 | 443 | -info 444 | : Set an X11 key to briefly display the active window's title (default: XK_w). 445 | 446 | goomwwm -info w 447 | 448 | -increase 449 | : Set an X11 key to incrementally resize the active window (default: shift+page_up). 450 | 451 | goomwwm -increase shift+page_up 452 | 453 | -left 454 | : Set an X11 key name to move the active window to the left in a 3x3 grid (default: XK_Left). 455 | 456 | goomwwm -left Left 457 | 458 | -mapmode 459 | : Control the window initial map focus mode (default: steal). 460 | 461 | goomwwm -mapmode steal 462 | 463 | Valid settings are: 464 | 465 | steal 466 | : new windows get focus. 467 | 468 | block 469 | : new windows do not get focus. 470 | 471 | -menubc 472 | : Set the border color (X11 named color or hex #rrggbb) for the window-switcher menu (default: #c0c0c0). 473 | 474 | goomwwm -menubc "#c0c0c0" 475 | 476 | -menubg 477 | : Set the background text color (X11 named color or hex #rrggbb) for the window-switcher menu (default: #f2f1f0). 478 | 479 | goomwwm -menubg "#f2f1f0" 480 | 481 | -menubgalt 482 | : Set the alternate background text color (X11 named color or hex #rrggbb) for the window-switcher menu (default: #e9e8e7). 483 | 484 | goomwwm -menubgalt "#e9e8e7" 485 | 486 | -menufg 487 | : Set the foreground text color (X11 named color or hex #rrggbb) for the window-switcher menu (default: #222222). 488 | 489 | goomwwm -menufg "#222222" 490 | 491 | -menufont 492 | : Xft font name for use by the window-switcher menu (default: mono-14). 493 | 494 | goomwwm -menufont monospace-14:medium 495 | 496 | -menuhlbg 497 | : Set the background text color (X11 named color or hex #rrggbb) for the highlighted item in the window-switcher menu (default: #005577). 498 | 499 | goomwwm -menufg "#005577" 500 | 501 | -menuhlfg 502 | : Set the foreground text color (X11 named color or hex #rrggbb) for the highlighted item in the window-switcher menu (default: #ffffff). 503 | 504 | goomwwm -menufg "#ffffff" 505 | 506 | -menulines 507 | : Maximum number of entries the window-switcher menu may show before scrolling (default: 25). 508 | 509 | goomwwm -menulines 25 510 | 511 | -menuselect 512 | : Control how menu items are selected (default: return). 513 | 514 | goomwwm -menuselect return 515 | 516 | Valid settings are: 517 | 518 | return 519 | : Menu stays open until item is selected with Enter/Return key. This is dmenu-like. 520 | 521 | modkeyup 522 | : Menu stays open until item is selected by releasing the modkey. This is classic Alt-Tab window switching behavior. 523 | 524 | -menuwidth 525 | : Set the width of the window-switcher menu as a percentage of the screen width if <= 100 (% symbol optional), or in pixels if >100 (default: 60%). 526 | 527 | goomwwm -menuwidth 60% 528 | goomwwm -menuwidth 800 529 | 530 | -minimize 531 | : Set an X11 key name to minimize a window (default: XK_slash). 532 | 533 | goomwwm -minimize slash 534 | 535 | -modkey 536 | : Change the modifier key mask to any combination of: control,mod1,mod2,mod3,mod4,mod5 (default: mod4). 537 | 538 | goomwwm -modkey control,mod1 539 | 540 | -only 541 | : Set an X11 key name to show only windows in the current tag, hiding everything else (default: XK_o). 542 | 543 | goomwwm -only o 544 | 545 | -onlyauto 546 | : Make **-only** behavior automatic after current tag switch. Note that while this setting makes tags behave pretty much like virtual desktops, it also reduces flexibility. 547 | 548 | goomwwm -onlyauto 549 | 550 | -placement 551 | : Control the position of new windows (default: any). 552 | 553 | goomwwm -placement any 554 | 555 | Valid settings are: 556 | 557 | any 558 | : Windows that specify or remember their placement are honored. Everything else is centered on the current monitor. 559 | 560 | center 561 | : Windows are centered on the current monitor. 562 | 563 | pointer 564 | : Windows are centered under the mouse pointer. 565 | 566 | -prefix 567 | : Set an X11 key name to act as a modal key combination that replaces the default modifier key for all other combinations (default: none). This is similar to the way key combinations work in **ratpoison** and GNU **screen**. 568 | 569 | goomwwm -prefix z 570 | 571 | Above, **Mod-z** would now need to preceed all other keys. For example, cycling windows would become preass and relases **Mod-z** then press **c**. 572 | 573 | Of course, **-prefix** can also be combined with **-modkey**: 574 | 575 | goomwwm -modkey control -prefix z 576 | 577 | Cycling windows would then become **Control-z** then **c**. 578 | 579 | Finally, if you press the prefix key combination by mistake, press the prefix key again to cancel. 580 | 581 | -quit 582 | : Set an X11 key name to exit the window manager (default: XK_Pause). This key must be **pressed twice** to take effect! 583 | 584 | goomwwm -quit Pause 585 | 586 | -raisemode 587 | : Control the window raise mode (default: focus). 588 | 589 | goomwwm -raisemode focus 590 | 591 | Valid settings are: 592 | 593 | focus 594 | : Window is raised on focus (default for -focusmode click). 595 | 596 | click 597 | : Window is raised on Mod-AnyButton click (default for -focusmode sloppy[tag]). 598 | 599 | -resizehints 600 | : How to handle windows that specify resize-increment hints (Default: smart). These are what can sometimes cause tiled terminals to have gaps around the edges. 601 | 602 | goomwwm -resizehints smart 603 | 604 | Valid settings are: 605 | 606 | all 607 | : All window hints are respected. 608 | 609 | none 610 | : No window hints are respected. Note that this does not prevent windows from sending a follow-up request to be resized to respect their hints. gnome-terminal and lxterminal both do this and may always show gaps. 611 | 612 | smart 613 | : Most window hints are respected, except for a few apps we know can handle having their hints ignored. At present, this is **xterm** and **urxvt**. 614 | 615 | (posix regex) 616 | : Implies smart mode. A regular expression to match the WM_CLASS of windows to ignore **smart** mode. By default this is "^(xterm|urxvt)$". Regex is case-insensitive using POSIX extended syntax. 617 | 618 | -right 619 | : Set an X11 key name to move the active window to the right in a 3x3 grid (default: XK_Right). 620 | 621 | goomwwm -right Right 622 | 623 | -rule 624 | : Define a global window control rule (default: none). This argument can be specified multiple times to define multiple rules. If a window matches multiple rules only the *last* rule specified is used. 625 | 626 | goomwwm -rule "firefox tag9" 627 | goomwwm -rule "xfce4-notifyd ignore" 628 | goomwwm -rule "xterm left,maximize_vert,medium" 629 | 630 | Rules always have the format: 631 | 632 | pattern flag[...,flagN] 633 | 634 | The **pattern** is a case-insensitive POSIX regular expression matched against a window's WM_CLASS, application name, or title fields (in that order). Alternatively, the pattern can be limited to one field by using **class:**, **name:**, or **title:** pattern prefixes (this is also faster): 635 | 636 | goomwwm -rule "class:firefox tag9" 637 | goomwwm -rule "name:xfce4-notifyd ignore" 638 | goomwwm -rule "title:xterm left,maximize_vert,medium" 639 | 640 | Valid **flags** are: 641 | 642 | ignore 643 | : Do not manage a window. Effectively makes a window behave as it the override_redirect flag is set. 644 | 645 | steal block 646 | : Allow or prevent a new widow taking focus. 647 | 648 | reset 649 | : Remove all EWMH states and H/V locks (useful for -ruleset). 650 | 651 | once 652 | : Allow a rule to execute only once (useful for -ruleset). 653 | 654 | minimize restore 655 | : Start window pre-minimzed, or restore a window on rule set switch. 656 | 657 | minimize_auto 658 | : Automatically minimize a window when it loses focus. 659 | 660 | tag1 tag2 tag3 tag4 tag5 tag6 tag7 tag8 tag9 661 | : Apply tags to a window when it first opens. If the current tag is not in the list the window will not be raised or allowed to take focus. 662 | 663 | monitor1 monitor2 monitor3 664 | : Place the window on a specific monitor. These are numbered based on what Xinerama thinks the monitor order should be (ie, usually the same screen numbers as defined in xorg.conf). 665 | 666 | above below fullscreen maximize_horz maximize_vert sticky skip_taskbar skip_pager 667 | : Apply respective _NET_WM_STATE_* to a window. 668 | 669 | raise lower 670 | : Pre-raise or lower a window in the stacking order. These only take effect for a blocked window. For unblocked windows that take focus, -raisemode takes precedence. 671 | 672 | left right top bottom 673 | : Align a window with a screen edge. May be combined. Top trumps bottom. Left trumps right. 674 | 675 | center pointer 676 | : Place a window center-screen or centered under the mouse pointer. 677 | 678 | small medium large cover expand contract 679 | : Set a window's initial size (same increments as PageUp/Down). May be combined. 680 | 681 | hlock vlock 682 | : Lock window horizontally or vertically. 683 | 684 | htile huntile vtile vuntile 685 | : Tile or untile a window with its fellows. 686 | 687 | snap_left snap_right snap_up snap_down 688 | : Immediately snap a window to another's edge. 689 | 690 | replace 691 | : Place a window in the same position as the active window. 692 | 693 | duplicate 694 | : Size a window to match the one beneath it. 695 | 696 | NxN N%xN% 697 | : Apply a specific size in pixels or percent of monitor size. 698 | 699 | 700 | Rules are not currently applied to transient windows (dialogs). 701 | 702 | -ruleset 703 | : Define a group of rules to execute on all windows in the current tag when selected by menu (default: none). See **-runruleset**. 704 | 705 | goomwwm -ruleset Name -rule ... -rule ... -ruleset Name2 -rule ... 706 | 707 | Or, in .goomwwmrc: 708 | 709 | ruleset Development Layout 710 | rule class:xterm right,bottom,small 711 | rule class:gvim left,maximize_vert,large 712 | 713 | ruleset Email/Chat Distractions 714 | rule class:pidgin left,bottom,small,snap_right 715 | rule class:chromium top,maximize_horz,large 716 | 717 | 718 | All **-ruleset** definitions need to come after the global **-rule** definitions on the command line, or in .goomwwmrc. 719 | 720 | Where global rules are autonomous and their order is not important, rulesets are more like mini scripts where rules are commands executed in order. Windows may therefore be affected by multiple rules in a ruleset. Use precise regex patterns to be safe. 721 | 722 | -runrule 723 | : Set an X11 key name to reapply any rule relevant to the active window (default: XK_comma). 724 | 725 | goomwwm -runrule comma 726 | 727 | -runruleset 728 | : Set an X11 key name to execute defined rule sets using a menu (default: XK_period). 729 | 730 | goomwwm -runrule period 731 | 732 | -shrink 733 | : Set an X11 key name to decrease the active window size (default: XK_Page_Down) through four basic sizes that tile well together: 1/9th, 1/4th, 4/9th, or fullscreen. 734 | 735 | goomwwm -shrink Page_Down 736 | 737 | -snapdown 738 | : Set an X11 key name to snap the active window downward to the nearest border. 739 | 740 | goomwwm -snapdown Shift+Down 741 | 742 | -snapleft 743 | : Set an X11 key name to snap the active window left to the nearest border. 744 | 745 | goomwwm -snapleft Shift+Left 746 | 747 | -snapright 748 | : Set an X11 key name to snap the active window right to the nearest border. 749 | 750 | goomwwm -snapright Shift+Right 751 | 752 | -snapup 753 | : Set an X11 key name to snap the active window upward to the nearest border. 754 | 755 | goomwwm -snapup Shift+Up 756 | 757 | -swapdown 758 | : Set an X11 key name to swap the active window with one below. 759 | 760 | goomwwm -swapdown Shift+Down 761 | 762 | -swapleft 763 | : Set an X11 key name to swap the active window with one to the left. 764 | 765 | goomwwm -swapleft Shift+Left 766 | 767 | -swapright 768 | : Set an X11 key name to swap the active window with one to the right. 769 | 770 | goomwwm -swapright Shift+Right 771 | 772 | -swapup 773 | : Set an X11 key name to swap the active window with one above. 774 | 775 | goomwwm -swapup Shift+Up 776 | 777 | -switch 778 | : Set an X11 key to start display window-switcher showing all open windows (default: XK_Tab). 779 | 780 | goomwwm -switch Tab 781 | 782 | -switcher 783 | : Command to run an alternate window-switcher (default: built-in menu). 784 | 785 | goomwwm -switcher dswitch 786 | 787 | -tag 788 | : Set an X11 key to toggle the active window's membership of the current tag (default: XK_t). 789 | 790 | goomwwm -tag t 791 | 792 | -tswitch 793 | : Set an X11 key to start display window-switcher showing only windows in the current tag (default: XK_grave). 794 | 795 | goomwwm -tswitch grave 796 | 797 | -tagnext 798 | : Set an X11 key to cycle tags forward (default: XK_m). 799 | 800 | goomwwm -tagnext m 801 | 802 | -tagprev 803 | : Set an X11 key to cycle tags in reverse (default: XK_n). 804 | 805 | goomwwm -tagprev n 806 | 807 | -titlebc 808 | : Set the border color (X11 named color or hex #rrggbb) for window titles (default: #c0c0c0). 809 | 810 | goomwwm -titlebc "#c0c0c0" 811 | 812 | -titlebg 813 | : Set the background text color (X11 named color or hex #rrggbb) for window titles (default: #f2f1f0). 814 | 815 | goomwwm -titlebg "#f2f1f0" 816 | 817 | -titlefg 818 | : Set the foreground text color (X11 named color or hex #rrggbb) for window titles (default: #222222). 819 | 820 | goomwwm -titlefg "#222222" 821 | 822 | -titlefont 823 | : Xft font name for use by window popup titles (default: sans-14). 824 | 825 | goomwwm -titlefont sans-14:medium 826 | 827 | -titlebar 828 | : Height in pixels of a window title bar or on/off (default: off). 829 | 830 | goomwwm -titlebar 18 831 | goomwwm -titlebar on 832 | goomwwm -titlebar off 833 | 834 | The **on** argument will auto-calculate titlebar height based on **-titlebarfont**. 835 | 836 | -titlebarfont 837 | : Xft font name for use by window title bars (default: sans-10). 838 | 839 | goomwwm -titlebarfont sans-10:medium 840 | 841 | -titlebarfocus 842 | : Set the text color of focused-window title bars (X11 named color or hex #rrggbb). 843 | 844 | goomwwm -titlebarfocus "#eeeeee" 845 | 846 | -titlebarblur 847 | : Set the text color of unfocused-window title bars (X11 named color or hex #rrggbb). 848 | 849 | goomwwm -titlebarblur "#eeeeee" 850 | 851 | -up 852 | : Set an X11 key name to move the active window upward in a 3x3 grid (default: XK_Up). 853 | 854 | goomwwm -up Up 855 | 856 | -undo 857 | : Set an X11 key to undo the last size/position change for the active window (default: XK_u). Undo is 10 levels deep. 858 | 859 | goomwwm -undo u 860 | 861 | -vlock 862 | : Set an X11 key name to toggle vertical move/resize lock for the active window (default: XK_Insert). 863 | 864 | goomwwm -vlock Insert 865 | 866 | -vmax 867 | : Set an X11 key name to toggle _NET_WM_STATE_MAXIMIXED_VERT for the active window (default: XK_Home). 868 | 869 | goomwwm -vmax Home 870 | 871 | -vtile 872 | : Set an X11 key to vertically tile the active window and other windows with the same tag, position, and size (default: XK_v). 873 | 874 | goomwwm -vtile h 875 | 876 | -vuntile 877 | : Set an X11 key to do the opposite of vtile. 878 | 879 | goomwwm -vuntile h 880 | 881 | -warpmode 882 | : Control whether the mouse pointer warps to a focused window (default: never). This setting can make focusmode **sloppy** more cooperative when focus is changed by means other than the mouse. 883 | 884 | goomwwm -warpmode focus 885 | 886 | Valid settings are: 887 | 888 | never 889 | : Pointer is never moved (default for -focusmode click). 890 | 891 | focus 892 | : Pointer is warped to a newly focused window (default for -focusmode sloppy[tag]). 893 | 894 | 895 | # OPTIONS (cli mode) 896 | 897 | When run with **-cli** (command line interface) goomwwm may be used to dispatch commands to another running instance of goomwwm. Valid arguments are: 898 | 899 | -duration 900 | : A time delay in seconds. Currently used only for **-notice**. 901 | 902 | goomwwm -cli -notice "Hello World" -duration 5 903 | 904 | -exec 905 | : Switch to another window manager in place (without restarting X). 906 | 907 | goomwwm -cli -exec dwm 908 | 909 | -findstart 910 | : Locate a window by class, name, or title. If not found, execute it. 911 | 912 | goomwwm -cli -findstart class:xterm 913 | 914 | -notice 915 | : Instruct goomwwm to display something via the popup message box. 916 | 917 | goomwwm -cli -notice "Hello World" 918 | 919 | -quit 920 | : Exit goomwwm. 921 | 922 | goomwwm -cli -quit 923 | 924 | -restart 925 | : Restart a running goomwwm instance in place (without restarting X). Useful for reloading .goomwwmrc or upgrading to a new version. 926 | 927 | goomwwm -cli -restart 928 | 929 | -rule 930 | : Execute a temporary rule on windows in the current tag. 931 | 932 | goomwwm -cli -rule "xterm large" 933 | 934 | -ruleset 935 | : Execute a rule set by name. 936 | 937 | goomwwm -cli -ruleset alpha 938 | 939 | # SEE ALSO 940 | 941 | **dmenu** (1) 942 | 943 | # AUTHOR 944 | 945 | Sean Pringle -------------------------------------------------------------------------------- /grab.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // bind to a keycode in all lock states 28 | void grab_keycode(unsigned int mask, KeyCode keycode) 29 | { 30 | XGrabKey(display, keycode, mask, root, True, GrabModeAsync, GrabModeAsync); 31 | XGrabKey(display, keycode, mask|LockMask, root, True, GrabModeAsync, GrabModeAsync); 32 | if (NumlockMask) 33 | { 34 | XGrabKey(display, keycode, mask|NumlockMask, root, True, GrabModeAsync, GrabModeAsync); 35 | XGrabKey(display, keycode, mask|NumlockMask|LockMask, root, True, GrabModeAsync, GrabModeAsync); 36 | } 37 | } 38 | 39 | // grab a MODKEY+key combo 40 | void grab_key(unsigned int mask, KeySym key) 41 | { 42 | grab_keycode(mask, XKeysymToKeycode(display, key)); 43 | int i, j, min_code, max_code, syms_per_code; 44 | // if xmodmap is in use to remap keycodes to keysyms, a simple XKeysymToKeycode 45 | // may not suffice here. so we also walk the entire map of keycodes and bind to 46 | // each code mapped to "key" 47 | XDisplayKeycodes(display, &min_code, &max_code); 48 | KeySym *map = XGetKeyboardMapping(display, min_code, max_code-min_code, &syms_per_code); 49 | for (i = 0; map && i < (max_code-min_code); i++) 50 | for (j = 0; j < syms_per_code; j++) 51 | if (key == map[i*syms_per_code+j]) 52 | grab_keycode(mask, i+min_code); 53 | if (map) XFree(map); 54 | } 55 | 56 | // run at startup and on MappingNotify 57 | void grab_keys_and_buttons() 58 | { 59 | int i; 60 | XUngrabKey(display, AnyKey, AnyModifier, root); 61 | // only grab keys if prefix mode is disabled (default) 62 | if (!config_prefix_mode) 63 | { 64 | // configurable keys 65 | for (i = 0; keymap[i]; i++) if (keymap[i] != XK_VoidSymbol) 66 | grab_key(keymodmap[i], keymap[i]); 67 | 68 | // 1-9 app keys 69 | for (i = 0; config_apps_keysyms[i]; i++) if (config_apps_patterns[i]) 70 | { grab_key(config_modkey, config_apps_keysyms[i]); grab_key(config_modkey|ShiftMask, config_apps_keysyms[i]); } 71 | 72 | // F1-F9 tag keys 73 | for (i = 0; config_tags_keysyms[i]; i++) 74 | { grab_key(config_modkey, config_tags_keysyms[i]); grab_key(config_modkey|ShiftMask, config_tags_keysyms[i]); } 75 | } 76 | // prefix mode key switches to XGrabKeyboard 77 | else grab_key(config_modkey, keymap[KEY_PREFIX]); 78 | // grab mouse buttons for click-to-focus. these get passed through to the windows 79 | // not binding on button4 which is usually wheel scroll 80 | XUngrabButton(display, AnyButton, AnyModifier, root); 81 | XGrabButton(display, Button1, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); 82 | XGrabButton(display, Button2, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); 83 | XGrabButton(display, Button3, AnyModifier, root, True, ButtonPressMask, GrabModeSync, GrabModeSync, None, None); 84 | } 85 | -------------------------------------------------------------------------------- /menu.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | void menu_draw(textbox *text, textbox **boxes, int max_lines, int selected, char **filtered) 28 | { 29 | int i; 30 | textbox_draw(text); 31 | for (i = 0; i < max_lines; i++) 32 | { 33 | textbox_font(boxes[i], config_menu_font, 34 | i == selected ? config_menu_hlfg: config_menu_fg, 35 | i == selected ? config_menu_hlbg: config_menu_bg); 36 | textbox_text(boxes[i], filtered[i] ? filtered[i]: ""); 37 | textbox_draw(boxes[i]); 38 | } 39 | } 40 | 41 | int menu(char **lines, char **input, char *prompt, int selected) 42 | { 43 | int line = -1, i, j, chosen = 0; 44 | workarea mon; monitor_active(&mon); 45 | 46 | int num_lines = 0; for (; lines[num_lines]; num_lines++); 47 | int max_lines = MIN(config_menu_lines, num_lines); 48 | selected = MAX(MIN(num_lines-1, selected), 0); 49 | 50 | int w = config_menu_width < 101 ? (mon.w/100)*config_menu_width: config_menu_width; 51 | int x = mon.x + (mon.w - w)/2; 52 | 53 | Window box = XCreateSimpleWindow(display, root, x, 0, w, 300, 1, color_get(config_menu_bc), color_get(config_menu_bg)); 54 | XSelectInput(display, box, ExposureMask); 55 | 56 | // make it an unmanaged window 57 | window_set_atom_prop(box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1); 58 | window_set_atom_prop(box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_DOCK], 1); 59 | 60 | // search text input 61 | textbox *text = textbox_create(box, TB_AUTOHEIGHT|TB_EDITABLE, 5, 5, w-10, 1, 62 | config_menu_font, config_menu_fg, config_menu_bg, "", prompt); 63 | textbox_show(text); 64 | 65 | int line_height = text->font->ascent + text->font->descent; 66 | int row_padding = line_height/10; 67 | int row_height = line_height + row_padding; 68 | 69 | // filtered list display 70 | textbox **boxes = allocate_clear(sizeof(textbox*) * max_lines); 71 | 72 | for (i = 0; i < max_lines; i++) 73 | { 74 | boxes[i] = textbox_create(box, TB_AUTOHEIGHT, 5, (i+1) * row_height + 5, w-10, 1, 75 | config_menu_font, config_menu_fg, config_menu_bg, lines[i], NULL); 76 | textbox_show(boxes[i]); 77 | } 78 | 79 | // filtered list 80 | char **filtered = allocate_clear(sizeof(char*) * max_lines); 81 | int *line_map = allocate_clear(sizeof(int) * max_lines); 82 | int filtered_lines = max_lines; 83 | 84 | for (i = 0; i < max_lines; i++) 85 | { 86 | filtered[i] = lines[i]; 87 | line_map[i] = i; 88 | } 89 | 90 | // resize window vertically to suit 91 | int h = row_height * (max_lines+1) + 10 - row_padding; 92 | int y = mon.y + (mon.h - h)/2; 93 | XMoveResizeWindow(display, box, x, y, w, h); 94 | XMapRaised(display, box); 95 | 96 | take_keyboard(box); 97 | for (;;) 98 | { 99 | XEvent ev; 100 | XNextEvent(display, &ev); 101 | 102 | if (ev.type == Expose) 103 | { 104 | while (XCheckTypedEvent(display, Expose, &ev)); 105 | menu_draw(text, boxes, max_lines, selected, filtered); 106 | } 107 | else 108 | if (ev.type == KeyPress) 109 | { 110 | while (XCheckTypedEvent(display, KeyPress, &ev)); 111 | latest = ev.xkey.time; 112 | 113 | int rc = textbox_keypress(text, &ev); 114 | if (rc < 0) 115 | { 116 | chosen = 1; 117 | break; 118 | } 119 | else 120 | if (rc) 121 | { 122 | // input changed 123 | for (i = 0, j = 0; i < num_lines && j < max_lines; i++) 124 | { 125 | if (strcasestr(lines[i], text->text)) 126 | { 127 | line_map[j] = i; 128 | filtered[j++] = lines[i]; 129 | } 130 | } 131 | filtered_lines = j; 132 | selected = MAX(0, MIN(selected, j-1)); 133 | for (; j < max_lines; j++) 134 | filtered[j] = NULL; 135 | } 136 | else 137 | { 138 | // unhandled key 139 | KeySym key = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, 0); 140 | 141 | if (key == XK_Escape) 142 | break; 143 | 144 | if (key == XK_Up) 145 | selected = selected ? MAX(0, selected-1): MAX(0, filtered_lines-1); 146 | 147 | if (key == XK_Down || key == XK_Tab || key == XK_grave) 148 | selected = selected < filtered_lines-1 ? MIN(filtered_lines-1, selected+1): 0; 149 | } 150 | menu_draw(text, boxes, max_lines, selected, filtered); 151 | } 152 | 153 | // check for modkeyup mode 154 | if (config_menu_select == MENUMODUP && !modkey_is_down()) 155 | { 156 | chosen = 1; 157 | break; 158 | } 159 | } 160 | release_keyboard(); 161 | 162 | if (chosen && filtered[selected]) 163 | line = line_map[selected]; 164 | 165 | if (line < 0 && input) 166 | *input = strdup(text->text); 167 | 168 | textbox_free(text); 169 | for (i = 0; i < max_lines; i++) 170 | textbox_free(boxes[i]); 171 | XDestroyWindow(display, box); 172 | free(filtered); 173 | free(line_map); 174 | 175 | return line; 176 | } 177 | 178 | // simple little text input prompt based on menu 179 | // really needs a title, or perhaps optional "prompt text>" 180 | char* prompt(char *ps) 181 | { 182 | char *input = NULL; 183 | 184 | workarea mon; monitor_active(&mon); 185 | 186 | int w = config_menu_width < 101 ? (mon.w/100)*config_menu_width: config_menu_width; 187 | int x = mon.x + (mon.w - w)/2; 188 | 189 | Window box = XCreateSimpleWindow(display, root, x, 0, w, 300, 1, color_get(config_menu_bc), color_get(config_menu_bg)); 190 | XSelectInput(display, box, ExposureMask); 191 | 192 | // make it an unmanaged window 193 | window_set_atom_prop(box, netatoms[_NET_WM_STATE], &netatoms[_NET_WM_STATE_ABOVE], 1); 194 | window_set_atom_prop(box, netatoms[_NET_WM_WINDOW_TYPE], &netatoms[_NET_WM_WINDOW_TYPE_DOCK], 1); 195 | 196 | // textbox 197 | textbox *text = textbox_create(box, TB_EDITABLE|TB_AUTOHEIGHT, 5, 5, w-10, 1, config_menu_font, config_menu_fg, config_menu_bg, "", ps); 198 | 199 | // textbox has computed line height for the supplied font 200 | // resize window vertically to suit 201 | int h = text->h + 10; 202 | int y = mon.y + (mon.h - h)/2; 203 | XMoveResizeWindow(display, box, x, y, w, h); 204 | 205 | textbox_show(text); 206 | XMapRaised(display, box); 207 | 208 | take_keyboard(box); 209 | for (;;) 210 | { 211 | XEvent ev; 212 | XNextEvent(display, &ev); 213 | 214 | if (ev.type == Expose) 215 | { 216 | while (XCheckTypedEvent(display, Expose, &ev)); 217 | textbox_draw(text); 218 | } 219 | else 220 | if (ev.type == KeyPress) 221 | { 222 | while (XCheckTypedEvent(display, KeyPress, &ev)); 223 | 224 | int rc = textbox_keypress(text, &ev); 225 | if (rc < 0) 226 | { 227 | // text entered 228 | input = strdup(text->text); 229 | break; 230 | } 231 | else 232 | if (rc) 233 | { 234 | // selection changed 235 | textbox_draw(text); 236 | } 237 | else 238 | { 239 | // unhandled key 240 | KeySym key = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, 0); 241 | if (key == XK_Escape) break; 242 | } 243 | } 244 | } 245 | release_keyboard(); 246 | 247 | textbox_free(text); 248 | XDestroyWindow(display, box); 249 | 250 | return input; 251 | } 252 | -------------------------------------------------------------------------------- /monitor.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // find the dimensions of the monitor displaying point x,y 28 | void monitor_dimensions(int x, int y, workarea *mon) 29 | { 30 | int monitors, i; 31 | memset(mon, 0, sizeof(workarea)); 32 | 33 | mon->w = WidthOfScreen(screen); 34 | mon->h = HeightOfScreen(screen); 35 | 36 | // locate the current monitor 37 | if (XineramaIsActive(display)) 38 | { 39 | XineramaScreenInfo *info = XineramaQueryScreens(display, &monitors); 40 | if (info) 41 | { 42 | for (i = 0; i < monitors; i++) 43 | { 44 | if (INTERSECT(x, y, 1, 1, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) 45 | { 46 | mon->x = info[i].x_org; mon->y = info[i].y_org; 47 | mon->w = info[i].width; mon->h = info[i].height; 48 | break; 49 | } 50 | } 51 | XFree(info); 52 | } 53 | } 54 | } 55 | 56 | // find the dimensions, EXCLUDING STRUTS, of the monitor displaying point x,y 57 | void monitor_dimensions_struts(int x, int y, workarea *mon) 58 | { 59 | int i; 60 | // check cache. we may be able to avoid walking all windows 61 | for (i = 0; i < sizeof(cache_monitor)/sizeof(workarea); i++) 62 | { 63 | workarea *cm = &cache_monitor[i]; if (!cm->w) continue; 64 | if (INTERSECT(x, y, 1, 1, cm->x-cm->l, cm->y-cm->t, cm->w+cm->l+cm->r, cm->h+cm->t+cm->b)) 65 | { 66 | memmove(mon, cm, sizeof(workarea)); 67 | return; 68 | } 69 | } 70 | 71 | monitor_dimensions(x, y, mon); 72 | 73 | // strut cardinals are relative to the root window size, which is not necessarily the monitor size 74 | XWindowAttributes *rattr = window_get_attributes(root); 75 | int left = 0, right = 0, top = 0, bottom = 0; 76 | 77 | Window win; 78 | // walk the open apps and check for struts 79 | // this is fairly lightweight thanks to some caches 80 | winlist_ascend(windows_in_play(), i, win) 81 | { 82 | XWindowAttributes *attr = window_get_attributes(win); 83 | if (attr && !attr->override_redirect 84 | && INTERSECT(attr->x, attr->y, attr->width, attr->height, mon->x, mon->y, mon->w, mon->h)) 85 | { 86 | unsigned long strut[12]; memset(strut, 0, sizeof(strut)); 87 | if (window_get_cardinal_prop(win, netatoms[_NET_WM_STRUT_PARTIAL], strut, 12) 88 | || window_get_cardinal_prop(win, netatoms[_NET_WM_STRUT], strut, 4)) 89 | { 90 | // we only pay attention to the first four params 91 | // this is no more complex that _NET_WM_STRUT, but newer stuff uses _PARTIAL 92 | left = MAX(left, strut[0]); right = MAX(right, strut[1]); 93 | top = MAX(top, strut[2]); bottom = MAX(bottom, strut[3]); 94 | } 95 | } 96 | } 97 | // limit struts on any side to 1/3 monitor size 98 | mon->l = MIN(mon->w/3, MAX(0, left-mon->x)); 99 | mon->r = MIN(mon->w/3, MAX(0, (mon->x+mon->w)-(rattr->width-right))); 100 | mon->t = MIN(mon->h/3, MAX(0, top-mon->y)); 101 | mon->b = MIN(mon->h/3, MAX(0, (mon->y+mon->h)-(rattr->height-bottom))); 102 | mon->x += mon->l; mon->y += mon->t; 103 | mon->w -= (mon->l+mon->r); 104 | mon->h -= (mon->t+mon->b); 105 | 106 | // update cache. strust change rarely, so this is long-lived. 107 | // see handle_propertynotify() also 108 | for (i = 0; i < sizeof(cache_monitor)/sizeof(workarea); i++) 109 | { 110 | workarea *cm = &cache_monitor[i]; 111 | if (!cm->w) 112 | { 113 | memmove(cm, mon, sizeof(workarea)); 114 | break; 115 | } 116 | } 117 | } 118 | 119 | // determine which monitor holds the active window, or failing that the mouse pointer 120 | void monitor_active(workarea *mon) 121 | { 122 | client *c = client_active(0); 123 | if (c) 124 | { 125 | client_extended_data(c); 126 | memmove(mon, &c->monitor, sizeof(workarea)); 127 | return; 128 | } 129 | int x, y; 130 | if (pointer_get(&x, &y)) 131 | { 132 | monitor_dimensions_struts(x, y, mon); 133 | return; 134 | } 135 | monitor_dimensions_struts(0, 0, mon); 136 | } 137 | -------------------------------------------------------------------------------- /proto.h: -------------------------------------------------------------------------------- 1 | box* box_create(Window parent, bitmap flags, short x, short y, short w, short h, char *color); 2 | void box_color(box *b, char *color); 3 | void box_moveresize(box *b, short x, short y, short w, short h); 4 | void box_show(box *b); 5 | void box_hide(box *b); 6 | void box_draw(box *b); 7 | void box_free(box *b); 8 | void cli_message(Atom atom, char *cmd); 9 | int cli_main(int argc, char *argv[]); 10 | void client_flush_state(client *c); 11 | int client_has_state(client *c, Atom state); 12 | void client_add_state(client *c, Atom state); 13 | void client_remove_state(client *c, Atom state); 14 | void client_remove_all_states(client *c); 15 | void client_set_state(client *c, Atom state, int on); 16 | void client_descriptive_data(client *c); 17 | void client_extended_data(client *c); 18 | int client_rule_match(client *c, winrule *r); 19 | client* client_create(Window win); 20 | client* client_recreate(Window w); 21 | void client_free(client *c); 22 | int clients_intersect(client *a, client *b); 23 | int client_protocol_event(client *c, Atom protocol); 24 | void client_close(client *c); 25 | int client_warp_check(client *c, int x, int y); 26 | void client_warp_pointer(client *c); 27 | void client_process_size_hints(client *c, int *x, int *y, int *w, int *h); 28 | void client_moveresize(client *c, unsigned int flags, int fx, int fy, int fw, int fh); 29 | void client_commit(client *c); 30 | void client_rollback(client *c); 31 | void client_save_position(client *c); 32 | void client_save_position_horz(client *c); 33 | void client_save_position_vert(client *c); 34 | void client_restore_position(client *c, unsigned int smart, int x, int y, int w, int h); 35 | void client_restore_position_horz(client *c, unsigned int smart, int x, int w); 36 | void client_restore_position_vert(client *c, unsigned int smart, int y, int h); 37 | winlist* clients_fully_visible(workarea *zone, unsigned int tag, Window ignore); 38 | winlist* clients_partly_visible(workarea *zone, unsigned int tag, Window ignore); 39 | void client_expand(client *c, int directions, int x1, int y1, int w1, int h1, int mx, int my, int mw, int mh); 40 | void client_contract(client *c, int directions); 41 | void client_snapto(client *c, int direction); 42 | void client_toggle_large(client *c, int side); 43 | void client_flash(client *c, char *color, int delay, int title); 44 | void client_stack_family(client *c, winlist *stack); 45 | void client_raise(client *c, int priority); 46 | void client_raise_under(client *c, client *under); 47 | void client_lower(client *c, int priority); 48 | void client_review_border(client *c); 49 | void client_review_nws_actions(client *c); 50 | void client_review_position(client *c); 51 | void client_review_desktop(client *c); 52 | void client_full_review(client *c); 53 | void client_redecorate(client *c); 54 | void client_deactivate(client *c, client *a); 55 | void client_activate(client *c, int raise, int warp); 56 | void client_set_wm_state(client *c, unsigned long state); 57 | unsigned long client_get_wm_state(client *c); 58 | client* client_active(unsigned int tag); 59 | void client_toggle_vlock(client *c); 60 | void client_toggle_hlock(client *c); 61 | void client_nws_fullscreen(client *c, int action); 62 | void client_nws_above(client *c, int action); 63 | void client_nws_below(client *c, int action); 64 | void client_nws_sticky(client *c, int action); 65 | void client_nws_maxvert(client *c, int action); 66 | void client_nws_maxhorz(client *c, int action); 67 | winlist* clients_tiled_horz_with(client *c); 68 | winlist* clients_tiled_vert_with(client *c); 69 | winlist* clients_tiled_with(client *c); 70 | void client_switch_to(client *c); 71 | void client_cycle(client *c); 72 | void client_htile(client *c); 73 | void client_huntile(client *c); 74 | void client_vtile(client *c); 75 | void client_vuntile(client *c); 76 | client* client_over_there_ish(client *c, int direction); 77 | void client_focusto(client *c, int direction); 78 | void client_swapto(client *c, int direction); 79 | void client_replace(client *c); 80 | void client_duplicate(client *c); 81 | void client_minimize(client *c); 82 | void client_restore(client *c); 83 | void client_shade(client *c); 84 | void client_reveal(client *c); 85 | void client_switcher(unsigned int tag); 86 | void client_toggle_tag(client *c, unsigned int tag, int flash); 87 | client* client_find(char *pattern); 88 | void client_start(char *pattern); 89 | void client_find_or_start(char *pattern); 90 | void client_rules_ewmh(client *c); 91 | void client_rules_monitor(client *c); 92 | void client_rules_moveresize(client *c); 93 | void client_rules_locks(client *c); 94 | void client_rules_tags(client *c); 95 | void client_rules_moveresize_post(client *c); 96 | void client_rules_apply(client *c, bool reset); 97 | void event_client_dump(client *c); 98 | void ewmh_client_list(); 99 | void ewmh_active_window(Window w); 100 | void ewmh_desktop_list(); 101 | int main(int argc, char *argv[]); 102 | void grab_keycode(unsigned int mask, KeyCode keycode); 103 | void grab_key(unsigned int mask, KeySym key); 104 | void grab_keys_and_buttons(); 105 | void handle_keypress(XEvent *ev); 106 | void handle_buttonpress(XEvent *ev); 107 | void handle_buttonrelease(XEvent *ev); 108 | void handle_motionnotify(XEvent *ev); 109 | void handle_createnotify(XEvent *ev); 110 | void handle_destroynotify(XEvent *ev); 111 | void handle_configurerequest(XEvent *ev); 112 | void handle_configurenotify(XEvent *ev); 113 | void handle_maprequest(XEvent *ev); 114 | void handle_mapnotify(XEvent *ev); 115 | void handle_unmapnotify(XEvent *ev); 116 | void handle_clientmessage(XEvent *ev); 117 | void handle_propertynotify(XEvent *ev); 118 | void handle_enternotify(XEvent *ev); 119 | void handle_mappingnotify(XEvent *ev); 120 | void handle_expose(XEvent *ev); 121 | void menu_draw(textbox *text, textbox **boxes, int max_lines, int selected, char **filtered); 122 | int menu(char **lines, char **input, char *prompt, int selected); 123 | char* prompt(char *ps); 124 | void monitor_dimensions(int x, int y, workarea *mon); 125 | void monitor_dimensions_struts(int x, int y, workarea *mon); 126 | void monitor_active(workarea *mon); 127 | int rule_parse(char *rulestr); 128 | void rule_free(winrule *rule); 129 | void ruleset_switcher(); 130 | void rulelist_apply(winrule *list); 131 | void rule_apply(winrule *rule); 132 | void rule_execute(char *rulestr); 133 | void ruleset_execute(char *name); 134 | unsigned int tag_to_desktop(unsigned int tag); 135 | unsigned int desktop_to_tag(unsigned int desktop); 136 | void tag_set_current(unsigned int tag); 137 | void tag_raise(unsigned int tag); 138 | void tag_auto_switch(); 139 | void tag_only(unsigned int tag); 140 | void tag_close(unsigned int tag); 141 | textbox* textbox_create(Window parent, bitmap flags, short x, short y, short w, short h, char *font, char *fg, char *bg, char *text, char *prompt); 142 | void textbox_font(textbox *tb, char *font, char *fg, char *bg); 143 | void textbox_extents(textbox *tb); 144 | void textbox_text(textbox *tb, char *text); 145 | void textbox_prompt(textbox *tb, char *text); 146 | void textbox_moveresize(textbox *tb, int x, int y, int w, int h); 147 | void textbox_show(textbox *tb); 148 | void textbox_hide(textbox *tb); 149 | void textbox_free(textbox *tb); 150 | void textbox_draw(textbox *tb); 151 | void textbox_cursor(textbox *tb, int pos); 152 | void textbox_cursor_inc(textbox *tb); 153 | void textbox_cursor_dec(textbox *tb); 154 | void textbox_cursor_home(textbox *tb); 155 | void textbox_cursor_end(textbox *tb); 156 | void textbox_insert(textbox *tb, int pos, char *str); 157 | void textbox_delete(textbox *tb, int pos, int dlen); 158 | void textbox_cursor_ins(textbox *tb, char c); 159 | void textbox_cursor_del(textbox *tb); 160 | void textbox_cursor_bkspc(textbox *tb); 161 | int textbox_keypress(textbox *tb, XEvent *ev); 162 | void* allocate(unsigned long bytes); 163 | void* allocate_clear(unsigned long bytes); 164 | void* reallocate(void *ptr, unsigned long bytes); 165 | char* strtrim(char *str); 166 | void catch_exit(int sig); 167 | int execsh(char *cmd); 168 | int find_arg(int argc, char *argv[], char *key); 169 | char* find_arg_str(int argc, char *argv[], char *key, char* def); 170 | int find_arg_int(int argc, char *argv[], char *key, int def); 171 | int regquick(char *pat, char *str); 172 | int in_array_keysym(KeySym *array, KeySym code); 173 | unsigned int color_get(const char *name); 174 | int pointer_get(int *x, int *y); 175 | int keycode_is_mod(unsigned int code); 176 | int modkey_is_down(); 177 | int take_keyboard(Window w); 178 | int take_pointer(Window w, unsigned long mask, Cursor cur); 179 | void release_keyboard(); 180 | void release_pointer(); 181 | void message_box(int delay, int x, int y, char *fgc, char *bgc, char *bc, char *txt); 182 | void notice(const char *fmt, ...); 183 | void notification(int delay, const char *fmt, ...); 184 | void event_log(const char *e, Window w); 185 | void event_note(const char *fmt, ...); 186 | void window_select(Window w); 187 | XWindowAttributes* window_get_attributes(Window w); 188 | int window_get_prop(Window w, Atom prop, Atom *type, int *items, void *buffer, int bytes); 189 | char* window_get_text_prop(Window w, Atom atom); 190 | int window_set_text_prop(Window w, Atom atom, char *txt); 191 | int window_get_atom_prop(Window w, Atom atom, Atom *list, int count); 192 | void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count); 193 | int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count); 194 | void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count); 195 | void window_unset_prop(Window w, Atom prop); 196 | int window_send_message(Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask); 197 | winlist* windows_in_play(); 198 | winlist* window_children(); 199 | int window_is_active(Window w); 200 | winlist* winlist_new(); 201 | int winlist_append(winlist *l, Window w, void *d); 202 | void winlist_prepend(winlist *l, Window w, void *d); 203 | void winlist_empty(winlist *l); 204 | void winlist_free(winlist *l); 205 | int winlist_find(winlist *l, Window w); 206 | int winlist_forget(winlist *l, Window w); 207 | void winlist_reverse(winlist *l); 208 | int oops(Display *d, XErrorEvent *ee); 209 | void reset_lazy_caches(); 210 | void reset_cache_xattr(); 211 | void reset_cache_client(); 212 | void reset_cache_inplay(); 213 | void setup_screen(); 214 | unsigned int parse_key_mask(char *keystr, unsigned int def); 215 | void setup_keyboard_options(int ac, char *av[]); 216 | void setup_general_options(int ac, char *av[]); 217 | void setup_rule_options(int ac, char *av[]); 218 | int wm_main(int argc, char *argv[]); 219 | -------------------------------------------------------------------------------- /rule.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // load a rule specified on cmd line or .goomwwmrc 28 | int rule_parse(char *rulestr) 29 | { 30 | winrule *new = allocate_clear(sizeof(winrule)); 31 | char *str = strdup(rulestr); strtrim(str); 32 | char *left = str, *right = str; 33 | // locate end of pattern 34 | while (*right && !isspace(*right)) right++; 35 | new->pattern = strndup(str, right-left); 36 | while (*right && isspace(*right)) right++; 37 | // walk over rule flags, space or command delimited 38 | while (*right && !isspace(*right)) 39 | { 40 | left = right; 41 | // scan for delimiters 42 | while (*right && !strchr(" ,\t", *right)) right++; 43 | if (right > left) 44 | { 45 | char flag[32]; memset(flag, 0, sizeof(flag)); 46 | strncpy(flag, left, MIN(sizeof(flag)-1, right-left)); 47 | // check for geometry 48 | if (regquick("^[0-9]*[%]*x[0-9]*[%]*$", flag)) 49 | { 50 | new->flags |= RULE_SIZE; 51 | char *p = flag; 52 | new->w = strtol(p, &p, 10); 53 | new->w_is_pct = (*p == '%') ? 1:0; 54 | if (new->w_is_pct) p++; 55 | if (*p == 'x') p++; 56 | new->h = strtol(p, &p, 10); 57 | new->h_is_pct = (*p == '%') ? 1:0; 58 | } else 59 | // check known flags 60 | { 61 | int i; for (i = 0; i < sizeof(rulemap)/sizeof(winrulemap); i++) 62 | if (!strcasecmp(flag, rulemap[i].name)) 63 | { new->flags |= rulemap[i].flag; break; } 64 | } 65 | } 66 | // skip delimiters 67 | while (*right && strchr(" ,\t", *right)) right++; 68 | } 69 | // prepare pattern regexes 70 | char *pat = new->pattern; 71 | if (regquick("^(class|name|title):", pat)) pat = strchr(pat, ':')+1; 72 | 73 | int ok = 0; 74 | if (regcomp(&new->re, pat, REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0) 75 | { 76 | new->next = config_rules; 77 | config_rules = new; 78 | ok = 1; 79 | } else 80 | { 81 | fprintf(stderr, "failed to compile regex: %s\n", pat); 82 | free(new->pattern); free(new); 83 | } 84 | free(str); 85 | return ok; 86 | } 87 | 88 | // release rule memory 89 | void rule_free(winrule *rule) 90 | { 91 | if (!rule) return; 92 | regfree(&rule->re); 93 | free(rule->pattern); 94 | free(rule); 95 | } 96 | 97 | // pick a ruleset to execute 98 | void ruleset_switcher() 99 | { 100 | int i, count = 0; char **list; winruleset *set; 101 | 102 | // count rulesets 103 | for (count = 0, set = config_rulesets; set; count++, set = set->next); 104 | list = allocate_clear(sizeof(char*) * (count+1)); // +1 NULL sell terminates 105 | // build a simple list of rule file names 106 | for (i = count-1, set = config_rulesets; set; i--, set = set->next) list[i] = basename(set->name); 107 | 108 | if (!fork()) 109 | { 110 | display = XOpenDisplay(0); 111 | XSync(display, True); 112 | int n = menu(list, NULL, "> ", 0); 113 | if (n >= 0 && list[n]) 114 | { 115 | cli_message(gatoms[GOOMWWM_RULESET], list[n]); 116 | usleep(300000); 117 | } 118 | exit(EXIT_SUCCESS); 119 | } 120 | free(list); 121 | } 122 | 123 | // apply a rule list to all windows in current_tag 124 | void rulelist_apply(winrule *list) 125 | { 126 | int i, done = 0; Window w; client *c; 127 | winrule *bak = config_rules; config_rules = list; 128 | tag_descend(i, w, c, current_tag) 129 | if (!done) 130 | { 131 | reset_cache_xattr(); 132 | reset_cache_client(); 133 | c = client_create(w); 134 | if (c) client_rules_apply(c, RULESDEF); 135 | if (c && c->is_ruled && c->rule && c->rule->flags & RULE_ONCE) done = 1; 136 | XSync(display, False); 137 | } 138 | clients_descend(windows_shaded, i, w, c) 139 | if (!done && c->manage && c->cache->tags & current_tag) 140 | { 141 | reset_cache_xattr(); 142 | reset_cache_client(); 143 | c = client_create(w); 144 | if (c) client_rules_apply(c, RULESDEF); 145 | if (c && c->is_ruled && c->rule && c->rule->flags & RULE_ONCE) done = 1; 146 | XSync(display, False); 147 | } 148 | clients_descend(windows_minimized, i, w, c) 149 | if (!done && c->manage && c->cache->tags & current_tag) 150 | { 151 | reset_cache_xattr(); 152 | reset_cache_client(); 153 | c = client_create(w); 154 | if (c) client_rules_apply(c, RULESDEF); 155 | if (c && c->is_ruled && c->rule && c->rule->flags & RULE_ONCE) done = 1; 156 | XSync(display, False); 157 | } 158 | config_rules = bak; 159 | } 160 | 161 | // apply a single rule to all windows in the current tag 162 | void rule_apply(winrule *rule) 163 | { 164 | winrule *next = rule->next; 165 | rule->next = NULL; 166 | rulelist_apply(rule); 167 | rule->next = next; 168 | } 169 | 170 | // execute a rule on open windows 171 | void rule_execute(char *rulestr) 172 | { 173 | if (rule_parse(rulestr)) 174 | { 175 | winrule *rule = config_rules; 176 | config_rules = rule->next; 177 | rule_apply(rule); 178 | rule_free(rule); 179 | } 180 | } 181 | 182 | // execute a ruleset on open windows 183 | void ruleset_execute(char *name) 184 | { 185 | winruleset *set = NULL; 186 | // find ruleset by index 187 | for (set = config_rulesets; set && strcasecmp(name, set->name); set = set->next); 188 | if (set && set->rules) 189 | { 190 | // bit odd. rules lists are lifos present, but it's more intuitive to process 191 | // rulesets in the order they were defined. should clean this up, but for now, 192 | // labouriously walk the list backwards 193 | winrule *rule = set->rules; 194 | while (rule->next) rule = rule->next; 195 | while (rule) 196 | { 197 | rule_apply(rule); 198 | winrule *prev = set->rules; 199 | while (prev && prev->next != rule) prev = prev->next; 200 | rule = prev; 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /screenshot-thumb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanpringle/goomwwm/25c9a12267e5345ab1b059966945067491e37b59/screenshot-thumb.jpg -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanpringle/goomwwm/25c9a12267e5345ab1b059966945067491e37b59/screenshot.jpg -------------------------------------------------------------------------------- /tag.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // for the benefit of EWMH type pagers, tag = desktop 28 | // but, since a window can have multiple tags... oh well 29 | unsigned int tag_to_desktop(unsigned int tag) 30 | { 31 | unsigned int i; for (i = 0; i < TAGS; i++) if (tag & (1<manage && c->cache->tags & tag) 56 | { client_reveal(c); shaded++; } 57 | if (shaded) 58 | { 59 | XSync(display, False); 60 | reset_cache_xattr(); 61 | reset_cache_client(); 62 | reset_cache_inplay(); 63 | } 64 | 65 | stack = winlist_new(); 66 | 67 | // locate windows with _NET_WM_STATE_ABOVE and _NET_WM_STATE_STICKY 68 | managed_descend(i, w, c) 69 | if (winlist_find(stack, w) < 0 && c->visible && c->trans == None 70 | && client_has_state(c, netatoms[_NET_WM_STATE_ABOVE]) 71 | && client_has_state(c, netatoms[_NET_WM_STATE_STICKY])) 72 | client_stack_family(c, stack); 73 | // locate windows with _NET_WM_STATE_ABOVE in this tag 74 | tag_descend(i, w, c, tag) 75 | if (winlist_find(stack, w) < 0 && c->visible && c->trans == None 76 | && client_has_state(c, netatoms[_NET_WM_STATE_ABOVE])) 77 | { client_stack_family(c, stack); found++; } 78 | // locate _NET_WM_WINDOW_TYPE_DOCK windows 79 | clients_descend(windows_in_play(), i, w, c) 80 | if (winlist_find(stack, w) < 0 && c->visible && c->trans == None 81 | && c->type == netatoms[_NET_WM_WINDOW_TYPE_DOCK]) 82 | client_stack_family(c, stack); 83 | // locate all other windows in the tag 84 | tag_descend(i, w, c, tag) 85 | if (winlist_find(stack, w) < 0 && c->trans == None) 86 | { client_stack_family(c, stack); found++; } 87 | 88 | // raise the top window in the stack 89 | if (stack->len) XRaiseWindow(display, stack->array[0]); 90 | // stack everything else, in order, underneath top window 91 | if (stack->len > 1) XRestackWindows(display, stack->array, stack->len); 92 | 93 | winlist_free(stack); 94 | tag_set_current(tag); 95 | if (config_only_auto) tag_only(tag); 96 | 97 | // focus the last-focused client in the tag 98 | clients_descend(windows_activated, i, w, c) if (c->cache->tags & tag) 99 | { client_activate(c, RAISE, WARPDEF); break; } 100 | 101 | // in case no windows are in the tag, show some activity 102 | if (found) notice("Tag %d", tag_to_desktop(tag)+1); 103 | else notice("Tag %d (empty!)", tag_to_desktop(tag)+1); 104 | } 105 | 106 | // check active client. if 107 | void tag_auto_switch() 108 | { 109 | client *c = client_active(0); 110 | if (c && c->cache->tags && !(c->cache->tags & current_tag)) 111 | { 112 | int i, n = 0; Window w; client *o; tag_descend(i, w, o, current_tag) n++; 113 | if (!n) tag_raise(desktop_to_tag(tag_to_desktop(c->cache->tags))); 114 | } 115 | } 116 | 117 | void tag_only(unsigned int tag) 118 | { 119 | int i; Window w; client *c; 120 | managed_descend(i, w, c) 121 | if (!(c->cache->tags & tag)) 122 | client_shade(c); 123 | } 124 | 125 | void tag_close(unsigned int tag) 126 | { 127 | int i; Window w; client *c; 128 | tag_descend(i, w, c, tag) client_close(c); 129 | } -------------------------------------------------------------------------------- /textbox.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // Xft text box, optionally editable 28 | textbox* textbox_create(Window parent, bitmap flags, short x, short y, short w, short h, char *font, char *fg, char *bg, char *text, char *prompt) 29 | { 30 | textbox *tb = allocate_clear(sizeof(textbox)); 31 | 32 | tb->flags = flags; 33 | tb->parent = parent; 34 | 35 | tb->x = x; tb->y = y; tb->w = MAX(1, w); tb->h = MAX(1, h); 36 | tb->window = XCreateSimpleWindow(display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, None, color_get(bg)); 37 | 38 | // need to preload the font to calc line height 39 | textbox_font(tb, font, fg, bg); 40 | 41 | tb->prompt = strdup(prompt ? prompt: ""); 42 | textbox_text(tb, text ? text: ""); 43 | 44 | // auto height/width modes get handled here 45 | textbox_moveresize(tb, tb->x, tb->y, tb->w, tb->h); 46 | 47 | // edit mode controls 48 | if (tb->flags & TB_EDITABLE) 49 | { 50 | tb->xim = XOpenIM(display, NULL, NULL, NULL); 51 | tb->xic = XCreateIC(tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, tb->window, XNFocusWindow, tb->window, NULL); 52 | } 53 | 54 | return tb; 55 | } 56 | 57 | // set an Xft font by name 58 | void textbox_font(textbox *tb, char *font, char *fg, char *bg) 59 | { 60 | if (tb->font) XftFontClose(display, tb->font); 61 | tb->font = XftFontOpenName(display, screen_id, font); 62 | 63 | XftColorAllocName(display, DefaultVisual(display, screen_id), DefaultColormap(display, screen_id), fg, &tb->color_fg); 64 | XftColorAllocName(display, DefaultVisual(display, screen_id), DefaultColormap(display, screen_id), bg, &tb->color_bg); 65 | } 66 | 67 | // outer code may need line height, width, etc 68 | void textbox_extents(textbox *tb) 69 | { 70 | int length = strlen(tb->text) + strlen(tb->prompt); 71 | char *line = alloca(length + 1); 72 | sprintf(line, "%s%s", tb->prompt, tb->text); 73 | XftTextExtents8(display, tb->font, (unsigned char*)line, length, &tb->extents); 74 | } 75 | 76 | // set the default text to display 77 | void textbox_text(textbox *tb, char *text) 78 | { 79 | if (tb->text) free(tb->text); 80 | tb->text = strdup(text); 81 | tb->cursor = MAX(0, MIN(strlen(text), tb->cursor)); 82 | textbox_extents(tb); 83 | } 84 | 85 | // set an input prompt for edit mode 86 | void textbox_prompt(textbox *tb, char *text) 87 | { 88 | if (tb->prompt) free(tb->prompt); 89 | tb->prompt = strdup(text); 90 | textbox_extents(tb); 91 | } 92 | 93 | // within the parent. handled auto width/height modes 94 | void textbox_moveresize(textbox *tb, int x, int y, int w, int h) 95 | { 96 | if (tb->flags & TB_AUTOHEIGHT) 97 | h = tb->font->ascent + tb->font->descent; 98 | 99 | if (tb->flags & TB_AUTOWIDTH) 100 | w = tb->extents.width; 101 | 102 | if (x != tb->x || y != tb->y || w != tb->w || h != tb->h) 103 | { 104 | tb->x = x; tb->y = y; tb->w = MAX(1, w); tb->h = MAX(1, h); 105 | XMoveResizeWindow(display, tb->window, tb->x, tb->y, tb->w, tb->h); 106 | } 107 | } 108 | 109 | void textbox_show(textbox *tb) 110 | { 111 | XMapWindow(display, tb->window); 112 | } 113 | 114 | void textbox_hide(textbox *tb) 115 | { 116 | XUnmapWindow(display, tb->window); 117 | } 118 | 119 | // will also unmap the window if still displayed 120 | void textbox_free(textbox *tb) 121 | { 122 | if (tb->flags & TB_EDITABLE) 123 | { 124 | XDestroyIC(tb->xic); 125 | XCloseIM(tb->xim); 126 | } 127 | 128 | if (tb->text) free(tb->text); 129 | if (tb->prompt) free(tb->prompt); 130 | if (tb->font) XftFontClose(display, tb->font); 131 | 132 | XDestroyWindow(display, tb->window); 133 | free(tb); 134 | } 135 | 136 | void textbox_draw(textbox *tb) 137 | { 138 | int i; 139 | XGlyphInfo extents; 140 | 141 | GC context = XCreateGC(display, tb->window, 0, 0); 142 | Pixmap canvas = XCreatePixmap(display, tb->window, tb->w, tb->h, DefaultDepth(display, screen_id)); 143 | XftDraw *draw = XftDrawCreate(display, canvas, DefaultVisual(display, screen_id), DefaultColormap(display, screen_id)); 144 | 145 | // clear canvas 146 | XftDrawRect(draw, &tb->color_bg, 0, 0, tb->w, tb->h); 147 | 148 | char *line = tb->text, 149 | *text = tb->text ? tb->text: "", 150 | *prompt = tb->prompt ? tb->prompt: ""; 151 | 152 | int text_len = strlen(text); 153 | int length = text_len; 154 | int line_height = tb->font->ascent + tb->font->descent; 155 | int line_width = 0; 156 | 157 | int cursor_x = 0; 158 | int cursor_offset = 0; 159 | int cursor_width = MAX(2, line_height/10); 160 | 161 | if (tb->flags & TB_EDITABLE) 162 | { 163 | int prompt_len = strlen(prompt); 164 | length = text_len + prompt_len; 165 | cursor_offset = MIN(tb->cursor + prompt_len, length); 166 | 167 | line = alloca(length + 10); 168 | sprintf(line, "%s%s", prompt, text); 169 | 170 | // replace spaces so XftTextExtents8 includes their width 171 | for (i = 0; i < length; i++) if (isspace(line[i])) line[i] = '_'; 172 | 173 | // calc cursor position 174 | XftTextExtents8(display, tb->font, (unsigned char*)line, cursor_offset, &extents); 175 | cursor_x = extents.width; 176 | 177 | // restore correct text string with spaces 178 | sprintf(line, "%s%s", prompt, text); 179 | } 180 | 181 | // calc full input text width 182 | XftTextExtents8(display, tb->font, (unsigned char*)line, length, &extents); 183 | line_width = extents.width; 184 | 185 | int x = 0, y = tb->font->ascent; 186 | if (tb->flags & TB_RIGHT) x = tb->w - line_width; 187 | if (tb->flags & TB_CENTER) x = (tb->w - line_width) / 2; 188 | 189 | // draw the text, including any prompt in edit mode 190 | XftDrawString8(draw, &tb->color_fg, tb->font, x, y, (unsigned char*)line, length); 191 | 192 | // draw the cursor 193 | if (tb->flags & TB_EDITABLE) 194 | XftDrawRect(draw, &tb->color_fg, cursor_x, 2, cursor_width, line_height-4); 195 | 196 | // flip canvas to window 197 | XCopyArea(display, canvas, tb->window, context, 0, 0, tb->w, tb->h, 0, 0); 198 | 199 | XFreeGC(display, context); 200 | XftDrawDestroy(draw); 201 | XFreePixmap(display, canvas); 202 | } 203 | 204 | // cursor handling for edit mode 205 | void textbox_cursor(textbox *tb, int pos) 206 | { 207 | tb->cursor = MAX(0, MIN(strlen(tb->text), pos)); 208 | } 209 | 210 | // move right 211 | void textbox_cursor_inc(textbox *tb) 212 | { 213 | textbox_cursor(tb, tb->cursor+1); 214 | } 215 | 216 | // move left 217 | void textbox_cursor_dec(textbox *tb) 218 | { 219 | textbox_cursor(tb, tb->cursor-1); 220 | } 221 | 222 | // beginning of line 223 | void textbox_cursor_home(textbox *tb) 224 | { 225 | tb->cursor = 0; 226 | } 227 | 228 | // end of line 229 | void textbox_cursor_end(textbox *tb) 230 | { 231 | tb->cursor = strlen(tb->text); 232 | } 233 | 234 | // insert text 235 | void textbox_insert(textbox *tb, int pos, char *str) 236 | { 237 | int len = strlen(tb->text), slen = strlen(str); 238 | pos = MAX(0, MIN(len, pos)); 239 | // expand buffer 240 | tb->text = reallocate(tb->text, len + slen + 1); 241 | // move everything after cursor upward 242 | char *at = tb->text + pos; 243 | memmove(at + slen, at, len - pos + 1); 244 | // insert new str 245 | memmove(at, str, slen); 246 | textbox_extents(tb); 247 | } 248 | 249 | // remove text 250 | void textbox_delete(textbox *tb, int pos, int dlen) 251 | { 252 | int len = strlen(tb->text); 253 | pos = MAX(0, MIN(len, pos)); 254 | // move everything after pos+dlen down 255 | char *at = tb->text + pos; 256 | memmove(at, at + dlen, len - pos); 257 | textbox_extents(tb); 258 | } 259 | 260 | // insert one character 261 | void textbox_cursor_ins(textbox *tb, char c) 262 | { 263 | char tmp[2] = { c, 0 }; 264 | textbox_insert(tb, tb->cursor, tmp); 265 | textbox_cursor_inc(tb); 266 | } 267 | 268 | // delete on character 269 | void textbox_cursor_del(textbox *tb) 270 | { 271 | textbox_delete(tb, tb->cursor, 1); 272 | } 273 | 274 | // back up and delete one character 275 | void textbox_cursor_bkspc(textbox *tb) 276 | { 277 | if (tb->cursor > 0) 278 | { 279 | textbox_cursor_dec(tb); 280 | textbox_cursor_del(tb); 281 | } 282 | } 283 | 284 | // handle a keypress in edit mode 285 | // 0 = unhandled 286 | // 1 = handled 287 | // -1 = handled and return pressed (finished) 288 | int textbox_keypress(textbox *tb, XEvent *ev) 289 | { 290 | KeySym key; Status stat; 291 | char pad[32]; int len; 292 | 293 | if (!(tb->flags & TB_EDITABLE)) return 0; 294 | 295 | len = XmbLookupString(tb->xic, &ev->xkey, pad, sizeof(pad), &key, &stat); 296 | pad[len] = 0; 297 | 298 | if (key == XK_Left) 299 | { 300 | textbox_cursor_dec(tb); 301 | return 1; 302 | } 303 | else 304 | if (key == XK_Right) 305 | { 306 | textbox_cursor_inc(tb); 307 | return 1; 308 | } 309 | else 310 | if (key == XK_Home) 311 | { 312 | textbox_cursor_home(tb); 313 | return 1; 314 | } 315 | else 316 | if (key == XK_End) 317 | { 318 | textbox_cursor_end(tb); 319 | return 1; 320 | } 321 | else 322 | if (key == XK_Delete) 323 | { 324 | textbox_cursor_del(tb); 325 | return 1; 326 | } 327 | else 328 | if (key == XK_BackSpace) 329 | { 330 | textbox_cursor_bkspc(tb); 331 | return 1; 332 | } 333 | else 334 | if (key == XK_Return) 335 | { 336 | return -1; 337 | } 338 | else 339 | if (!iscntrl(*pad)) 340 | { 341 | textbox_insert(tb, tb->cursor, pad); 342 | textbox_cursor_inc(tb); 343 | return 1; 344 | } 345 | return 0; 346 | } 347 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | void* allocate(unsigned long bytes) 28 | { 29 | bytes = MAX(1, bytes); 30 | void *ptr = malloc(bytes); 31 | if (!ptr) 32 | { 33 | fprintf(stderr, "malloc failed!\n"); 34 | exit(EXIT_FAILURE); 35 | } 36 | return ptr; 37 | } 38 | 39 | void* allocate_clear(unsigned long bytes) 40 | { 41 | void *ptr = allocate(bytes); 42 | memset(ptr, 0, bytes); 43 | return ptr; 44 | } 45 | 46 | void* reallocate(void *ptr, unsigned long bytes) 47 | { 48 | bytes = MAX(1, bytes); 49 | ptr = ptr ? realloc(ptr, bytes): malloc(bytes); 50 | if (!ptr) 51 | { 52 | fprintf(stderr, "realloc failed!\n"); 53 | exit(EXIT_FAILURE); 54 | } 55 | return ptr; 56 | } 57 | 58 | // trim string in place 59 | char* strtrim(char *str) 60 | { 61 | int i = 0, j = 0; 62 | while (isspace(str[i])) i++; 63 | while (str[i]) str[j++] = str[i++]; 64 | while (isspace(str[--j])); 65 | str[++j] = '\0'; 66 | return str; 67 | } 68 | 69 | double timestamp() 70 | { 71 | struct timeval tv; gettimeofday(&tv, NULL); 72 | return tv.tv_sec + (double)tv.tv_usec/1000000; 73 | } 74 | 75 | void catch_exit(int sig) 76 | { 77 | while (0 < waitpid(-1, NULL, WNOHANG)); 78 | } 79 | 80 | int execsh(char *cmd) 81 | { 82 | // use sh for args parsing 83 | return execlp("/bin/sh", "sh", "-c", cmd, NULL); 84 | } 85 | 86 | // execute sub-process 87 | pid_t exec_cmd(char *cmd) 88 | { 89 | if (!cmd || !cmd[0]) return -1; 90 | signal(SIGCHLD, catch_exit); 91 | pid_t pid = fork(); 92 | if (!pid) 93 | { 94 | setsid(); 95 | execsh(cmd); 96 | exit(EXIT_FAILURE); 97 | } 98 | return pid; 99 | } 100 | 101 | // cli arg handling 102 | int find_arg(int argc, char *argv[], char *key) 103 | { 104 | int i; for (i = 0; i < argc && strcasecmp(argv[i], key); i++); 105 | return i < argc ? i: -1; 106 | } 107 | 108 | char* find_arg_str(int argc, char *argv[], char *key, char* def) 109 | { 110 | int i = find_arg(argc, argv, key); 111 | return (i > 0 && i < argc-1) ? argv[i+1]: def; 112 | } 113 | 114 | int find_arg_int(int argc, char *argv[], char *key, int def) 115 | { 116 | int i = find_arg(argc, argv, key); 117 | return (i > 0 && i < argc-1) ? strtol(argv[i+1], NULL, 10): def; 118 | } 119 | 120 | // once-off regex match. don't use for repeat matching; compile instead 121 | int regquick(char *pat, char *str) 122 | { 123 | regex_t re; int r = 0; 124 | if (regcomp(&re, pat, REG_EXTENDED|REG_ICASE|REG_NOSUB) == 0) 125 | { 126 | r = regexec(&re, str, 0, NULL, 0) == 0 ?1:0; 127 | regfree(&re); 128 | } 129 | return r; 130 | } 131 | 132 | // true if keysym exists in array 133 | int in_array_keysym(KeySym *array, KeySym code) 134 | { 135 | int i; for (i = 0; array[i]; i++) 136 | if (array[i] == code) return i; 137 | return -1; 138 | } 139 | 140 | // allocate a pixel value for an X named color 141 | unsigned int color_get(const char *name) 142 | { 143 | XColor color; 144 | Colormap map = DefaultColormap(display, DefaultScreen(display)); 145 | return XAllocNamedColor(display, map, name, &color, &color) ? color.pixel: None; 146 | } 147 | 148 | // find mouse pointer location 149 | int pointer_get(int *x, int *y) 150 | { 151 | *x = 0; *y = 0; 152 | Window rr, cr; int rxr, ryr, wxr, wyr; unsigned int mr; 153 | if (XQueryPointer(display, root, &rr, &cr, &rxr, &ryr, &wxr, &wyr, &mr)) 154 | { 155 | *x = rxr; *y = ryr; 156 | return 1; 157 | } 158 | return 0; 159 | } 160 | 161 | // true if a keycode matches one of our modkeys 162 | int keycode_is_mod(unsigned int code) 163 | { 164 | int k; for (k = 0; config_modkeycodes[k]; k++) 165 | if (config_modkeycodes[k] == code) 166 | return 1; 167 | return 0; 168 | } 169 | 170 | // check whether our modkeys are currently pressed 171 | int modkey_is_down() 172 | { 173 | char keys[32]; int i, j; 174 | XQueryKeymap(display, keys); 175 | for (i = 0; i < 32; i++) 176 | { 177 | if (!keys[i]) continue; 178 | unsigned int bits = keys[i]; 179 | for (j = 0; j < 8; j++) 180 | if (bits & 1<window, TB_CENTER|TB_AUTOHEIGHT|TB_AUTOWIDTH, 228 | 8, 5, 1, 1, config_title_font, config_title_fg, config_title_bg, txt, NULL); 229 | 230 | box_moveresize(b, 231 | MIN(mon.x+mon.w-text->w-26, MAX(mon.x+26, x - text->w/2)), 232 | MIN(mon.y+mon.h-text->h-20, MAX(mon.y+20, y - text->h/2)), 233 | text->w + 16, text->h + 10); 234 | 235 | XSelectInput(display, b->window, ExposureMask); 236 | 237 | textbox_show(text); 238 | box_show(b); 239 | 240 | double stamp = timestamp(); 241 | while ((timestamp()-stamp) < (double)delay/1000) 242 | { 243 | if (XPending(display)) 244 | { 245 | XEvent ev; 246 | XNextEvent(display, &ev); 247 | 248 | if (ev.type == Expose) 249 | textbox_draw(text); 250 | } 251 | usleep(10000); // 10ms 252 | } 253 | 254 | textbox_free(text); 255 | box_free(b); 256 | 257 | exit(EXIT_SUCCESS); 258 | } 259 | 260 | // bottom right of screen 261 | void notice(const char *fmt, ...) 262 | { 263 | char txt[100]; va_list ap; 264 | va_start(ap,fmt); vsnprintf(txt, 100, fmt, ap); va_end(ap); 265 | workarea mon; monitor_active(&mon); 266 | message_box(SAYMS, mon.x+mon.w-1, mon.y+mon.h-1, config_title_fg, config_title_bg, config_title_bc, txt); 267 | } 268 | 269 | // bottom left of screen 270 | void notification(int delay, const char *fmt, ...) 271 | { 272 | char txt[100]; va_list ap; 273 | va_start(ap,fmt); vsnprintf(txt, 100, fmt, ap); va_end(ap); 274 | workarea mon; monitor_active(&mon); 275 | message_box(delay, mon.x, mon.y+mon.h-1, config_title_fg, config_title_bg, config_title_bc, txt); 276 | } 277 | 278 | #ifdef DEBUG 279 | void event_log(const char *e, Window w) 280 | { 281 | XClassHint chint; 282 | fprintf(stderr, "\n%s: %x", e, (unsigned int)w); 283 | if (w != None && XGetClassHint(display, w, &chint)) 284 | { 285 | fprintf(stderr, " %s", chint.res_class); 286 | XFree(chint.res_class); XFree(chint.res_name); 287 | } 288 | fprintf(stderr, "\n"); 289 | fflush(stderr); 290 | } 291 | #else 292 | #define event_log(...) 293 | #endif 294 | 295 | #ifdef DEBUG 296 | void event_note(const char *fmt, ...) 297 | { 298 | fprintf(stderr, "\t"); 299 | va_list ap; va_start(ap,fmt); vfprintf(stderr, fmt, ap); va_end(ap); 300 | fprintf(stderr, "\n"); 301 | } 302 | #else 303 | #define event_note(...) 304 | #endif 305 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #define VERSION "dev" -------------------------------------------------------------------------------- /window.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // events we're interested in 28 | void window_select(Window w) 29 | { 30 | XSelectInput(display, w, EnterWindowMask | LeaveWindowMask | FocusChangeMask | PropertyChangeMask); 31 | } 32 | 33 | // XGetWindowAttributes with caching 34 | XWindowAttributes* window_get_attributes(Window w) 35 | { 36 | int idx = winlist_find(cache_xattr, w); 37 | if (idx < 0) 38 | { 39 | XWindowAttributes *cattr = allocate(sizeof(XWindowAttributes)); 40 | if (XGetWindowAttributes(display, w, cattr)) 41 | { 42 | winlist_append(cache_xattr, w, cattr); 43 | return cattr; 44 | } 45 | free(cattr); 46 | return NULL; 47 | } 48 | return cache_xattr->data[idx]; 49 | } 50 | 51 | // retrieve a property of any type from a window 52 | int window_get_prop(Window w, Atom prop, Atom *type, int *items, void *buffer, int bytes) 53 | { 54 | Atom _type; if (!type) type = &_type; 55 | int _items; if (!items) items = &_items; 56 | int format; unsigned long nitems, nbytes; unsigned char *ret = NULL; 57 | memset(buffer, 0, bytes); 58 | 59 | if (XGetWindowProperty(display, w, prop, 0, bytes/4, False, AnyPropertyType, type, 60 | &format, &nitems, &nbytes, &ret) == Success && ret && *type != None && format) 61 | { 62 | if (format == 8) memmove(buffer, ret, MIN(bytes, nitems)); 63 | if (format == 16) memmove(buffer, ret, MIN(bytes, nitems * sizeof(short))); 64 | if (format == 32) memmove(buffer, ret, MIN(bytes, nitems * sizeof(long))); 65 | *items = (int)nitems; XFree(ret); 66 | return 1; 67 | } 68 | return 0; 69 | } 70 | 71 | // retrieve a text property from a window 72 | // technically we could use window_get_prop(), but this is better for character set support 73 | char* window_get_text_prop(Window w, Atom atom) 74 | { 75 | XTextProperty prop; char *res = NULL; 76 | char **list = NULL; int count; 77 | if (XGetTextProperty(display, w, &prop, atom) && prop.value && prop.nitems) 78 | { 79 | if (prop.encoding == XA_STRING) 80 | { 81 | res = allocate(strlen((char*)prop.value)+1); 82 | strcpy(res, (char*)prop.value); 83 | } 84 | else 85 | if (XmbTextPropertyToTextList(display, &prop, &list, &count) >= Success && count > 0 && *list) 86 | { 87 | res = allocate(strlen(*list)+1); 88 | strcpy(res, *list); 89 | XFreeStringList(list); 90 | } 91 | } 92 | if (prop.value) XFree(prop.value); 93 | return res; 94 | } 95 | 96 | int window_set_text_prop(Window w, Atom atom, char *txt) 97 | { 98 | XTextProperty prop; 99 | if (XStringListToTextProperty(&txt, 1, &prop)) 100 | { 101 | XSetTextProperty(display, w, &prop, atom); 102 | XFree(prop.value); 103 | } 104 | return 0; 105 | } 106 | 107 | int window_get_atom_prop(Window w, Atom atom, Atom *list, int count) 108 | { 109 | Atom type; int items; 110 | return window_get_prop(w, atom, &type, &items, list, count*sizeof(Atom)) && type == XA_ATOM ? items:0; 111 | } 112 | 113 | void window_set_atom_prop(Window w, Atom prop, Atom *atoms, int count) 114 | { 115 | XChangeProperty(display, w, prop, XA_ATOM, 32, PropModeReplace, (unsigned char*)atoms, count); 116 | } 117 | 118 | int window_get_cardinal_prop(Window w, Atom atom, unsigned long *list, int count) 119 | { 120 | Atom type; int items; 121 | return window_get_prop(w, atom, &type, &items, list, count*sizeof(unsigned long)) && type == XA_CARDINAL ? items:0; 122 | } 123 | 124 | void window_set_cardinal_prop(Window w, Atom prop, unsigned long *values, int count) 125 | { 126 | XChangeProperty(display, w, prop, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)values, count); 127 | } 128 | 129 | void window_unset_prop(Window w, Atom prop) 130 | { 131 | XDeleteProperty(display, w, prop); 132 | } 133 | 134 | // a ClientMessage 135 | // some things, like the built-in window switcher, use an EWMH ClientMessage 136 | // also, older WM_PROTOCOLS type stuff calls this 137 | int window_send_message(Window target, Window subject, Atom atom, unsigned long protocol, unsigned long mask) 138 | { 139 | XEvent e; memset(&e, 0, sizeof(XEvent)); 140 | e.xclient.type = ClientMessage; 141 | e.xclient.message_type = atom; e.xclient.window = subject; 142 | e.xclient.data.l[0] = protocol; e.xclient.data.l[1] = latest; 143 | e.xclient.send_event = True; e.xclient.format = 32; 144 | int r = XSendEvent(display, target, False, mask, &e) ?1:0; 145 | XFlush(display); 146 | return r; 147 | } 148 | 149 | // top-level, visible windows. DOES include non-managable docks/panels 150 | winlist* windows_in_play() 151 | { 152 | if (cache_inplay->len) return cache_inplay; 153 | 154 | unsigned int nwins; int i; Window w1, w2, *wins; 155 | if (XQueryTree(display, root, &w1, &w2, &wins, &nwins) && wins) 156 | { 157 | for (i = 0; i < nwins; i++) 158 | { 159 | XWindowAttributes *attr = window_get_attributes(wins[i]); 160 | if (attr && attr->override_redirect == False && attr->map_state == IsViewable) 161 | winlist_append(cache_inplay, wins[i], NULL); 162 | } 163 | } 164 | if (wins) XFree(wins); 165 | return cache_inplay; 166 | } 167 | 168 | // top-level windows, visible or not. DOES include non-managable docks/panels 169 | winlist* window_children() 170 | { 171 | winlist *l = winlist_new(); 172 | unsigned int nwins; int i; Window w1, w2, *wins; 173 | if (XQueryTree(display, root, &w1, &w2, &wins, &nwins) && wins) 174 | { 175 | for (i = 0; i < nwins; i++) 176 | { 177 | XWindowAttributes *attr = window_get_attributes(wins[i]); 178 | if (attr && attr->override_redirect == False && (attr->map_state == IsUnmapped || attr->map_state == IsViewable)) 179 | winlist_append(l, wins[i], NULL); 180 | } 181 | } 182 | if (wins) XFree(wins); 183 | return l; 184 | } 185 | 186 | // the window on top of windows_activated list was the last one we activated 187 | // assume this is still the active one... seems to work most of the time! 188 | // if this is wrong, worst case scenario is focus manages to revert to root 189 | int window_is_active(Window w) 190 | { 191 | return windows_activated->len && w == windows_activated->array[windows_activated->len-1] ?1:0; 192 | } 193 | -------------------------------------------------------------------------------- /winlist.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | winlist* winlist_new() 28 | { 29 | winlist *l = allocate(sizeof(winlist)); l->len = 0; 30 | l->array = allocate(sizeof(Window) * (WINLIST+1)); 31 | l->data = allocate(sizeof(void*) * (WINLIST+1)); 32 | return l; 33 | } 34 | 35 | int winlist_append(winlist *l, Window w, void *d) 36 | { 37 | if (l->len > 0 && !(l->len % WINLIST)) 38 | { 39 | l->array = reallocate(l->array, sizeof(Window) * (l->len+WINLIST+1)); 40 | l->data = reallocate(l->data, sizeof(void*) * (l->len+WINLIST+1)); 41 | } 42 | l->data[l->len] = d; 43 | l->array[l->len++] = w; 44 | return l->len-1; 45 | } 46 | 47 | void winlist_prepend(winlist *l, Window w, void *d) 48 | { 49 | winlist_append(l, None, NULL); 50 | memmove(&l->array[1], &l->array[0], sizeof(Window) * (l->len-1)); 51 | memmove(&l->data[1], &l->data[0], sizeof(void*) * (l->len-1)); 52 | l->array[0] = w; 53 | l->data[0] = d; 54 | } 55 | 56 | void winlist_empty(winlist *l) 57 | { 58 | while (l->len > 0) free(l->data[--(l->len)]); 59 | } 60 | 61 | void winlist_free(winlist *l) 62 | { 63 | winlist_empty(l); free(l->array); free(l->data); free(l); 64 | } 65 | 66 | int winlist_find(winlist *l, Window w) 67 | { 68 | // iterate backwards. theory is: windows most often accessed will be 69 | // nearer the end. testing with kcachegrind seems to support this... 70 | int i; Window o; winlist_descend(l, i, o) if (w == o) return i; 71 | return -1; 72 | } 73 | 74 | int winlist_forget(winlist *l, Window w) 75 | { 76 | int i, j; 77 | for (i = 0, j = 0; i < l->len; i++, j++) 78 | { 79 | l->array[j] = l->array[i]; 80 | l->data[j] = l->data[i]; 81 | if (l->array[i] == w) { free(l->data[i]); j--; } 82 | } 83 | l->len -= (i-j); 84 | return j != i ?1:0; 85 | } 86 | 87 | void winlist_reverse(winlist *l) 88 | { 89 | int i, j; 90 | for (i = 0, j = l->len-1; i < j; i++, j--) 91 | { 92 | Window w = l->array[i]; void *d = l->data[i]; 93 | l->array[i] = l->array[j]; l->data[i] = l->data[j]; 94 | l->array[j] = w; l->data[j] = d; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /wm.c: -------------------------------------------------------------------------------- 1 | /* GoomwWM, Get out of my way, Window Manager! 2 | 3 | MIT/X11 License 4 | Copyright (c) 2012 Sean Pringle 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | */ 26 | 27 | // X error handler 28 | int oops(Display *d, XErrorEvent *ee) 29 | { 30 | if (ee->error_code == BadWindow 31 | || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 32 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 33 | || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) 34 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 35 | ) return 0; 36 | fprintf(stderr, "error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); 37 | return xerror(display, ee); 38 | } 39 | 40 | // slow human interation resets some caches. theory is: if for some reason a stale 41 | // cache is affecting goomwwm behavior, any interaction should fix it. since 42 | // human-generated are so rare, this doesn't really affect the cache usefulness... 43 | void reset_lazy_caches() 44 | { 45 | memset(cache_monitor, 0, sizeof(cache_monitor)); 46 | } 47 | void reset_cache_xattr() 48 | { 49 | winlist_empty(cache_xattr); 50 | } 51 | void reset_cache_client() 52 | { 53 | int i; Window w; 54 | winlist_ascend(cache_client, i, w) 55 | client_free(cache_client->data[i]); 56 | cache_client->len = 0; 57 | } 58 | void reset_cache_inplay() 59 | { 60 | winlist_empty(cache_inplay); 61 | } 62 | 63 | // an X screen. may have multiple monitors, xinerama, etc 64 | void setup_screen() 65 | { 66 | int i; Window w; 67 | supporting = XCreateSimpleWindow(display, root, 0, 0, 1, 1, 0, 0, 0); 68 | unsigned long pid = getpid(); 69 | 70 | // EWMH 71 | XChangeProperty(display, root, netatoms[_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char*)netatoms, NETATOMS); 72 | 73 | // ewmh supporting wm 74 | XChangeProperty(display, root, netatoms[_NET_SUPPORTING_WM_CHECK], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&supporting, 1); 75 | XChangeProperty(display, supporting, netatoms[_NET_SUPPORTING_WM_CHECK], XA_WINDOW, 32, PropModeReplace, (unsigned char*)&supporting, 1); 76 | XChangeProperty(display, supporting, netatoms[_NET_WM_NAME], XA_STRING, 8, PropModeReplace, (const unsigned char*)"GoomwWM", 6); 77 | XChangeProperty(display, supporting, netatoms[_NET_WM_PID], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&pid, 1); 78 | 79 | // become the window manager here 80 | XSelectInput(display, root, StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask); 81 | 82 | // setup any existing windows 83 | winlist *l = window_children(); 84 | winlist_ascend(l, i, w) 85 | { 86 | wincache *cache = allocate_clear(sizeof(wincache)); 87 | winlist_append(windows, w, cache); 88 | client *c = client_create(w); 89 | if (c && c->manage && (c->visible || client_get_wm_state(c) == IconicState)) 90 | { 91 | window_select(c->window); 92 | winlist_append(c->visible ? windows_activated: windows_shaded, c->window, NULL); 93 | client_full_review(c); 94 | } 95 | } 96 | winlist_free(l); 97 | // activate and focus top window 98 | client_active(0); 99 | ewmh_client_list(); 100 | ewmh_desktop_list(); 101 | } 102 | 103 | // identify modifiers in a key combination string 104 | unsigned int parse_key_mask(char *keystr, unsigned int def) 105 | { 106 | unsigned int modkey = 0; 107 | if (keystr) 108 | { 109 | if (strcasestr(keystr, "shift")) modkey |= ShiftMask; 110 | if (strcasestr(keystr, "control")) modkey |= ControlMask; 111 | if (strcasestr(keystr, "mod1")) modkey |= Mod1Mask; 112 | if (strcasestr(keystr, "mod2")) modkey |= Mod2Mask; 113 | if (strcasestr(keystr, "mod3")) modkey |= Mod3Mask; 114 | if (strcasestr(keystr, "mod4")) modkey |= Mod4Mask; 115 | if (strcasestr(keystr, "mod5")) modkey |= Mod5Mask; 116 | } 117 | if (!(modkey & (ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))) 118 | modkey |= def; 119 | return modkey; 120 | } 121 | 122 | void setup_keyboard_options(int ac, char *av[]) 123 | { 124 | int i, j; 125 | 126 | // determine modkey 127 | char *modkeys = find_arg_str(ac, av, "-modkey", NULL); 128 | config_modkey = parse_key_mask(modkeys, MODKEY); 129 | 130 | // determine numlock mask so we can bind on keys with and without it 131 | XModifierKeymap *modmap = XGetModifierMapping(display); 132 | for (i = 0; i < 8; i++) 133 | for (j = 0; j < (int)modmap->max_keypermod; j++) 134 | if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(display, XK_Num_Lock)) 135 | { NumlockMask = (1<max_keypermod; j++) 140 | config_modkeycodes[i++] = modmap->modifiermap[2*modmap->max_keypermod+j]; 141 | if (config_modkey & Mod1Mask) 142 | for (j = 0; i < MAXMODCODES && j < (int)modmap->max_keypermod; j++) 143 | config_modkeycodes[i++] = modmap->modifiermap[3*modmap->max_keypermod+j]; 144 | if (config_modkey & Mod2Mask) 145 | for (j = 0; i < MAXMODCODES && j < (int)modmap->max_keypermod; j++) 146 | config_modkeycodes[i++] = modmap->modifiermap[4*modmap->max_keypermod+j]; 147 | if (config_modkey & Mod3Mask) 148 | for (j = 0; i < MAXMODCODES && j < (int)modmap->max_keypermod; j++) 149 | config_modkeycodes[i++] = modmap->modifiermap[5*modmap->max_keypermod+j]; 150 | if (config_modkey & Mod4Mask) 151 | for (j = 0; i < MAXMODCODES && j < (int)modmap->max_keypermod; j++) 152 | config_modkeycodes[i++] = modmap->modifiermap[6*modmap->max_keypermod+j]; 153 | if (config_modkey & Mod5Mask) 154 | for (j = 0; i < MAXMODCODES && j < (int)modmap->max_keypermod; j++) 155 | config_modkeycodes[i++] = modmap->modifiermap[7*modmap->max_keypermod+j]; 156 | XFreeModifiermap(modmap); 157 | 158 | // everything defaults to modkey 159 | for (i = 0; keyargs[i]; i++) 160 | keymodmap[i] |= config_modkey; 161 | 162 | // custom keys 163 | for (i = 0; keyargs[i]; i++) 164 | { 165 | char tmp[32]; strcpy(tmp, keyargs[i]); 166 | char *key = find_arg_str(ac, av, strtrim(tmp), NULL); 167 | if (!key) continue; 168 | 169 | unsigned int mask = parse_key_mask(key, config_modkey); 170 | if (strcasestr(key, "nomod")) mask = 0; 171 | if (strrchr(key, '-')) key = strrchr(key, '-')+1; 172 | if (strrchr(key, '+')) key = strrchr(key, '+')+1; 173 | KeySym sym = XStringToKeysym(key); 174 | if (sym == NoSymbol) 175 | { 176 | fprintf(stderr, "unknown key: %s\n", key); 177 | continue; 178 | } 179 | // remove existing refs to this key, so only one action is bound 180 | for (j = 0; keymap[j]; j++) 181 | if (keymap[j] == sym && keymodmap[j] == mask) 182 | keymap[j] = XK_VoidSymbol; 183 | keymap[i] = sym; 184 | keymodmap[i] = mask; 185 | } 186 | 187 | // check for prefix key mode 188 | config_prefix_mode = keymap[KEY_PREFIX] == XK_VoidSymbol ? NOPREFIX: PREFIX; 189 | prefix_cursor = XCreateFontCursor(display, XC_icon); 190 | } 191 | 192 | void setup_general_options(int ac, char *av[]) 193 | { 194 | int i; 195 | char *mode; 196 | 197 | // border colors 198 | config_border_focus = find_arg_str(ac, av, "-focus", FOCUS); 199 | config_border_blur = find_arg_str(ac, av, "-blur", BLUR); 200 | config_border_attention = find_arg_str(ac, av, "-attention", ATTENTION); 201 | 202 | // border width in pixels 203 | config_border_width = MAX(0, find_arg_int(ac, av, "-border", BORDER)); 204 | 205 | // window flashing 206 | config_flash_on = find_arg_str(ac, av, "-flashon", FLASHON); 207 | config_flash_off = find_arg_str(ac, av, "-flashoff", FLASHOFF); 208 | config_flash_width = MAX(config_border_width, find_arg_int(ac, av, "-flashpx", FLASHPX)); 209 | config_flash_ms = MAX(FLASHMS, find_arg_int(ac, av, "-flashms", FLASHMS)); 210 | 211 | // customizable keys 212 | config_switcher = find_arg_str(ac, av, "-switcher", SWITCHER); 213 | config_launcher = find_arg_str(ac, av, "-launcher", LAUNCHER); 214 | 215 | // window switcher 216 | config_menu_width = find_arg_int(ac, av, "-menuwidth", MENUWIDTH); 217 | config_menu_lines = find_arg_int(ac, av, "-menulines", MENULINES); 218 | config_menu_font = find_arg_str(ac, av, "-menufont", MENUXFTFONT); 219 | config_menu_fg = find_arg_str(ac, av, "-menufg", MENUFG); 220 | config_menu_bg = find_arg_str(ac, av, "-menubg", MENUBG); 221 | config_menu_bgalt = find_arg_str(ac, av, "-menubgalt", MENUBGALT); 222 | config_menu_hlfg = find_arg_str(ac, av, "-menuhlfg", MENUHLFG); 223 | config_menu_hlbg = find_arg_str(ac, av, "-menuhlbg", MENUHLBG); 224 | config_menu_bc = find_arg_str(ac, av, "-menubc", MENUBC); 225 | 226 | // popup window titles 227 | config_title_font = find_arg_str(ac, av, "-titlefont", TITLEXFTFONT); 228 | config_title_fg = find_arg_str(ac, av, "-titlefg", TITLEFG); 229 | config_title_bg = find_arg_str(ac, av, "-titlebg", TITLEBG); 230 | config_title_bc = find_arg_str(ac, av, "-titlebc", TITLEBC); 231 | 232 | // title bars 233 | config_titlebar_font = find_arg_str(ac, av, "-titlebarfont", TITLEBARXFTFONT); 234 | config_titlebar_focus = find_arg_str(ac, av, "-titlebarfocus", TITLEBARFOCUS); 235 | config_titlebar_blur = find_arg_str(ac, av, "-titlebarblur", TITLEBARBLUR); 236 | 237 | mode = find_arg_str(ac, av, "-titlebar", TITLEBAR); 238 | // check for specific height in pixels. any non-numeric string means 0 239 | config_titlebar_height = atoi(mode); 240 | if (!strcasecmp(mode, "on")) 241 | { 242 | textbox *tb = textbox_create(root, TB_AUTOHEIGHT, 0, 0, 1, 1, config_titlebar_font, "white", "black", NULL, NULL); 243 | config_titlebar_height = tb->font->ascent + tb->font->descent + config_border_width; 244 | textbox_free(tb); 245 | } 246 | 247 | // flash title mode 248 | config_flash_title = 0; 249 | mode = find_arg_str(ac, av, "-flashtitle", "hide"); 250 | if (!strcasecmp(mode, "show")) config_flash_title = 1; 251 | 252 | // focus mode 253 | config_focus_mode = FOCUSCLICK; 254 | mode = find_arg_str(ac, av, "-focusmode", "click"); 255 | if (!strcasecmp(mode, "sloppy")) config_focus_mode = FOCUSSLOPPY; 256 | if (!strcasecmp(mode, "sloppytag")) config_focus_mode = FOCUSSLOPPYTAG; 257 | 258 | // raise mode 259 | config_raise_mode = RAISEFOCUS; 260 | mode = find_arg_str(ac, av, "-raisemode", config_focus_mode == FOCUSCLICK ? "focus": "click"); 261 | if (!strcasecmp(mode, "click")) config_raise_mode = RAISECLICK; 262 | 263 | // warp mode 264 | config_warp_mode = WARPNEVER; 265 | mode = find_arg_str(ac, av, "-warpmode", config_focus_mode == FOCUSCLICK ? "never": "focus"); 266 | if (!strcasecmp(mode, "focus")) config_warp_mode = WARPFOCUS; 267 | if (!strcasecmp(mode, "follow")) config_warp_mode = WARPFOLLOW; 268 | 269 | // steal mode 270 | config_map_mode = MAPSTEAL; 271 | mode = find_arg_str(ac, av, "-mapmode", "steal"); 272 | if (!strcasecmp(mode, "block")) config_map_mode = MAPBLOCK; 273 | 274 | // activation mode 275 | config_tile_mode = TILESMART; 276 | mode = find_arg_str(ac, av, "-tilemode", "smart"); 277 | if (!strcasecmp(mode, "none")) config_tile_mode = TILENONE; 278 | 279 | // new-window placement mode 280 | config_window_placement = PLACEANY; 281 | mode = find_arg_str(ac, av, "-placement", "any"); 282 | if (!strcasecmp(mode, "center")) config_window_placement = PLACECENTER; 283 | if (!strcasecmp(mode, "pointer")) config_window_placement = PLACEPOINTER; 284 | 285 | // autohide non-current tags 286 | config_only_auto = find_arg(ac, av, "-onlyauto") >= 0 ? 1:0; 287 | 288 | // resize hints mode 289 | config_resize_inc = SMARTRESIZEINC; 290 | config_resizeinc_ignore = SMARTRESIZEINC_IGNORE; 291 | mode = find_arg_str(ac, av, "-resizehints", "smart"); 292 | if (!strcasecmp(mode, "all")) config_resize_inc = RESIZEINC; 293 | else if (!strcasecmp(mode, "none")) config_resize_inc = NORESIZEINC; 294 | else if (strcasecmp(mode, "smart")) config_resizeinc_ignore = mode; 295 | 296 | // menu select mode 297 | config_menu_select = MENURETURN; 298 | if (!config_prefix_mode) 299 | { 300 | mode = find_arg_str(ac, av, "-menuselect", "return"); 301 | if (!strcasecmp(mode, "modkeyup")) config_menu_select = MENUMODUP; 302 | } 303 | 304 | // optionally swap tag and app key 305 | mode = find_arg_str(ac, av, "-appkeys", "numbers"); 306 | if (!strcasecmp(mode, "functions")) 307 | { 308 | for (i = 0; i < sizeof(config_apps_keysyms)/sizeof(KeySym); i++) 309 | { 310 | KeySym k = config_apps_keysyms[i]; 311 | config_apps_keysyms[i] = config_tags_keysyms[i]; 312 | config_tags_keysyms[i] = k; 313 | } 314 | } 315 | 316 | // app_find_or_start() keys 317 | for (i = 0; config_apps_keysyms[i]; i++) 318 | { 319 | char tmp[3]; sprintf(tmp, "-%d", i); 320 | config_apps_patterns[i ? i-1: 9] = find_arg_str(ac, av, tmp, NULL); 321 | } 322 | } 323 | 324 | void setup_rule_options(int ac, char *av[]) 325 | { 326 | int i; 327 | // load window rules 328 | // put rules in a default ruleset 329 | config_rulesets = allocate_clear(sizeof(winruleset)); 330 | config_rulesets->name = strdup("[default rules]"); 331 | for (i = 0; i < ac; i++) 332 | { 333 | // load any other rule sets 334 | if (!strcasecmp(av[i], "-ruleset") && i < ac-1) 335 | { 336 | config_rulesets->rules = config_rules; 337 | config_rules = NULL; 338 | winruleset *set = allocate_clear(sizeof(winruleset)); 339 | set->name = strdup(av[++i]); 340 | set->next = config_rulesets; 341 | config_rulesets = set; 342 | } 343 | else 344 | if (!strcasecmp(av[i], "-rule") && i < ac-1) 345 | rule_parse(av[++i]); 346 | } 347 | config_rulesets->rules = config_rules; 348 | 349 | // default to first rule set 350 | winruleset *set = config_rulesets; 351 | while (set->next) set = set->next; 352 | config_rules = set->rules; 353 | } 354 | 355 | // window manager 356 | int wm_main(int argc, char *argv[]) 357 | { 358 | int i; XEvent ev; 359 | 360 | // prepare to fall back on ~/.goomwwmrc 361 | char *conf_home = NULL, *home = getenv("HOME"); 362 | if (home) 363 | { 364 | conf_home = allocate_clear(1024); 365 | sprintf(conf_home, "%s/%s", home, CONFIGFILE); 366 | } 367 | 368 | // prepare args and merge conf file args 369 | int ac = argc; char **av = argv, *conf; 370 | if ((conf = find_arg_str(argc, argv, "-config", conf_home))) 371 | { 372 | // new list for both sets of args 373 | av = allocate(sizeof(char*) * ac); 374 | for (i = 0; i < ac; i++) av[i] = argv[i]; 375 | // parse config line by line 376 | FILE *f = fopen(conf, "r"); 377 | if (!f) 378 | fprintf(stderr, "could not open %s\n", conf); 379 | else 380 | { 381 | char *line = allocate_clear(1024); 382 | // yes, +1. see hyphen prepend below 383 | while (fgets(line+1, 1023, f)) 384 | { 385 | strtrim(line+1); 386 | // comment or empty line 387 | if (!line[1] || line[1] == '#') continue; 388 | // nope, got a config var! 389 | av = reallocate(av, sizeof(char*)*(ac+2)); 390 | char *p = line; *p++ = '-'; 391 | // find end of arg name 392 | while (*p && !isspace(*p)) p++; 393 | *p++ = 0; av[ac++] = strdup(line); 394 | // find arg value, if it exists 395 | strtrim(p); if (*p) av[ac++] = strdup(p); 396 | } 397 | fclose(f); 398 | free(line); 399 | } 400 | } 401 | free(conf_home); 402 | #ifdef DEBUG 403 | for (i = 0; i < ac; i++) 404 | printf("arg: [%s]\n", av[i]); 405 | #endif 406 | // caches to reduce X server round trips during a single event 407 | cache_client = winlist_new(); 408 | cache_xattr = winlist_new(); 409 | cache_inplay = winlist_new(); 410 | memset(cache_monitor, 0, sizeof(cache_monitor)); 411 | 412 | // window tracking 413 | windows = winlist_new(); 414 | windows_activated = winlist_new(); 415 | windows_minimized = winlist_new(); 416 | windows_shaded = winlist_new(); 417 | 418 | // do this before setting error handler, so it fails if other wm in place 419 | XSelectInput(display, DefaultRootWindow(display), SubstructureRedirectMask); 420 | XSync(display, False); xerror = XSetErrorHandler(oops); XSync(display, False); 421 | 422 | setup_keyboard_options(ac, av); 423 | setup_general_options(ac, av); 424 | setup_rule_options(ac, av); 425 | setup_screen(); 426 | grab_keys_and_buttons(); 427 | 428 | // auto start stuff 429 | if (!fork()) 430 | { 431 | display = XOpenDisplay(0); 432 | for (i = 0; i < ac-1; i++) 433 | { 434 | if (!strcasecmp(av[i], "-exec")) exec_cmd(av[i+1]); 435 | else if (!strcasecmp(av[i], "-auto")) 436 | { 437 | client *a = client_find(av[i+1]); 438 | if (!a) client_start(av[i+1]); 439 | } 440 | } 441 | exit(EXIT_SUCCESS); 442 | } 443 | 444 | // be polite 445 | notice("Get out of my way, Window Manager!"); 446 | reset_lazy_caches(); 447 | 448 | // main event loop 449 | for(;;) 450 | { 451 | reset_cache_xattr(); 452 | 453 | // block and wait for something 454 | XNextEvent(display, &ev); 455 | if (ev.type == MappingNotify) handle_mappingnotify(&ev); 456 | if (ev.xany.window == None) continue; 457 | 458 | if (ev.type == KeyPress) handle_keypress(&ev); 459 | else if (ev.type == ButtonPress) handle_buttonpress(&ev); 460 | else if (ev.type == ButtonRelease) handle_buttonrelease(&ev); 461 | else if (ev.type == MotionNotify) handle_motionnotify(&ev); 462 | else if (ev.type == CreateNotify) handle_createnotify(&ev); 463 | else if (ev.type == DestroyNotify) handle_destroynotify(&ev); 464 | else if (ev.type == ConfigureRequest) handle_configurerequest(&ev); 465 | else if (ev.type == ConfigureNotify) handle_configurenotify(&ev); 466 | else if (ev.type == MapRequest) handle_maprequest(&ev); 467 | else if (ev.type == MapNotify) handle_mapnotify(&ev); 468 | else if (ev.type == UnmapNotify) handle_unmapnotify(&ev); 469 | else if (ev.type == ClientMessage) handle_clientmessage(&ev); 470 | else if (ev.type == PropertyNotify) handle_propertynotify(&ev); 471 | else if (ev.type == EnterNotify) handle_enternotify(&ev); 472 | else if (ev.type == Expose) handle_expose(&ev); 473 | #ifdef DEBUG 474 | else fprintf(stderr, "unhandled event %d: %x\n", ev.type, (unsigned int)ev.xany.window); 475 | catch_exit(0); 476 | #endif 477 | } 478 | return EXIT_SUCCESS; 479 | } 480 | --------------------------------------------------------------------------------