├── .gitignore ├── ChangeLog ├── Makefile ├── README.md ├── config.h.def └── dminiwm.c /.gitignore: -------------------------------------------------------------------------------- 1 | dminiwm 2 | config.h 3 | *.o 4 | *.swp 5 | *~ 6 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 8/12/14 2 | Added option to set a different mode for each desktop in config.h 3 | 4 | 10/4/14 5 | Add locale so fonts in firefox and maybe others work 6 | 7 | 16/5/13 8 | Better ordering of windows when changing desktops 9 | 10 | 8/5/13 11 | Set desktop app opens on by name 12 | 13 | 7/4/13 14 | Swap master with two windows open 15 | 16 | 6/4/13 17 | Minimise config.h.def 18 | 19 | 10/3/13 20 | Combine move_left and move_right functions into move_sideways 21 | 22 | 7/3/13 23 | Check for a already running window manager 24 | 25 | 5/10/12 26 | Changed client_to_desktop to keep client values 27 | 28 | 25/9/12 29 | Fix window positioning in vertical 30 | Remove extra XUnmapWindow in client_to_desktop() 31 | 32 | 24/9/12 33 | Fix for existing window/s not being unmapped in fullscreem mode 34 | when new window opened 35 | Bar is toggled on a per desktop basis 36 | 37 | 21/9/12 38 | Fix for fullscreen app being too tall 39 | Add border to first opened window in stacking mode 40 | 41 | 9/8/12 42 | In vertical or horizontal mode can now add windows to the master area 43 | with keyboard shortcuts. 44 | 45 | 7/8/12 46 | Changed grid. First column and second window are resizable 47 | Fixed kill client and quit 48 | The order windows are focused in is remembered - 49 | makes a difference in stacking mode ! 50 | Option to output info to stdout for use in bipolarbar etc. 51 | 52 | 29/6/12 53 | Don't allow unmanaged windows to be moved 54 | Quieter quit 55 | 56 | 27/4/12 57 | Added stacking mode 58 | 59 | 7/4/12 60 | Fix for transient windows going off the screen 61 | 62 | 25/3/12 63 | Fix for firefox menu disappearing after deleting an entry 64 | 65 | 7/3/12 66 | Multiple transient windows are handled properly now 67 | 68 | 6/3/12 69 | Added option to have new window at the top of the stack when using attach aside 70 | 71 | 28/2/12 72 | Removed remembering the last focused window 73 | 74 | 27/2/12 75 | If FOLLOW_MOUSE = 0 the cursor will be moved to the last focused window 76 | when a window is removed or the desktop changed 77 | 78 | 26/2/12 79 | Added option to config to not have the panel shown if there's a window open 80 | 81 | 6/2/12 82 | Bugfixes. Fullscreen mode last window always mapped now. 83 | tabbed firefox doesn't open empty windows on unfocused desktops 84 | transient windows are always floating 85 | 86 | 9/1/12 87 | Master area is reset if second last window is removed 88 | 89 | 7/1/12 90 | Fix for window sizes after the window at the top of the stack has been resized 91 | Added limits on the area of the master window 92 | Fix for thunderbird write window( and others probably) just unmapping 93 | 94 | 27/12/11 95 | A better send_kill_signal function so we can have a better quit function 96 | 97 | 24/12/11 98 | Added keyboard shortcut to toggle the panel 99 | Changed quit function to exit on first try 100 | 101 | 23/12/11 102 | Added keyboard shortcut options to follow window to new desktop or not 103 | 104 | 8/12/11 105 | Fix for fullscreen mode flicker 106 | 107 | 28/11/11 108 | Replaced next/prev_desktop function with shorter rotate_desktop function 109 | (thanks to c00kiemon5ter) 110 | 111 | 28/11/11 112 | Added last desktop function for switching back to the last opened desktop 113 | (thanks to c00kimon5ter) 114 | 115 | 26/11/11 116 | Added option to click on a window to focus it 117 | (thanks to richo4) 118 | 119 | 21/11/11 120 | Fix for transient windows not being managed 121 | 122 | 15/11/11 123 | Fix for when trying to swap master with only one window 124 | 125 | 23/10/11 126 | Fine tuned the popup window management so notification windows aren't managed either 127 | 128 | 15/10/11 129 | Added having applications start on specified desktop 130 | 131 | 13/10/11 132 | Added ability to use grow_window/shrink_window in grid mode with 3 or 4 windows 133 | 134 | 13/10/11 135 | Popup windows now aren't managed/added to the stack 136 | 137 | 10/10/11 138 | Reapplied setting master_size relevant to default mode 139 | 140 | 8/10/11 141 | For only one window or fullscreen mode there is no border 142 | 143 | 8/10/11 144 | Fixed fullscreen mplayer 145 | 146 | 8/10/11 147 | Removed useless gaps when in grid mode 148 | 149 | 16/9/11 150 | Added back the option to have the panel at the top 151 | Fine tuned the grid tiling mode 152 | 153 | 13/9/11 154 | Added back horizontal tiling, follow mouse and follow window 155 | 156 | 12/9/11 157 | Returned to catwm tiling methods - with the ability to resize the window at the top of the stack 158 | 159 | 9/9/11 160 | Fine tuned the next/prev desktop and the destroy notify functions 161 | 162 | 8/9/11 163 | Have a proper destroy notify function now that removes destroyed windows from 164 | the window manager stack for the desktop they were on. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS+= -Wall 2 | LDADD+= -lX11 3 | LDFLAGS= 4 | EXEC=dminiwm 5 | 6 | PREFIX?= /usr/local 7 | BINDIR?= $(PREFIX)/bin 8 | 9 | CC=gcc 10 | 11 | all: $(EXEC) 12 | 13 | dminiwm: dminiwm.o 14 | $(CC) $(LDFLAGS) -s -Os -o $@ $+ $(LDADD) 15 | 16 | install: all 17 | install -Dm 755 dminiwm $(DESTDIR)$(BINDIR)/dminiwm 18 | 19 | clean: 20 | rm -fv dminiwm *.o 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##dminiwm 2 | ### it's minimal and dynamic 3 | 4 | I started this from catwm 31/12/10 ( https://bbs.archlinux.org/viewtopic.php?id=100215&p=1 ) 5 | See dminiwm.c for thanks and licensing. 6 | Screenshots and ramblings/updates at https://bbs.archlinux.org/viewtopic.php?id=126463 7 | 8 | 9 | ###Summary 10 | ------- 11 | 12 | 13 | **dminiwm** is a very minimal and lightweight dynamic tiling window manager. 14 | 15 | > I will try to stay under 1000 SLOC. 16 | 17 | > Currently under 960 lines with the config file included. 18 | 19 | 20 | ###Modes 21 | ----- 22 | 23 | It allows the "normal" method of tiling window managers(with the new window as the master) 24 | or with the new window opened at the bottom or the top of the stack. 25 | 26 | *There's vertical tiling mode:* 27 | 28 | -------------- 29 | | | W | 30 | | |___| 31 | | Master | | 32 | | |___| 33 | | | | 34 | -------------- 35 | 36 | *Horizontal tiling mode:* 37 | 38 | ------------- 39 | | | 40 | | Master | 41 | |-----------| 42 | | W | | | 43 | ------------- 44 | 45 | *Grid tiling mode:* 46 | 47 | ------------- 48 | | | W | 49 | |Master| | 50 | |------|----| 51 | | | | 52 | ------------- 53 | 54 | *Stacking mode:* 55 | 56 | ------------- 57 | | _______ | 58 | | | ___ | | 59 | | | |___| | | 60 | | |_______| | 61 | ------------- 62 | 63 | 64 | *Fullscreen mode*(which you'll know when you see it) 65 | 66 | All accessible with keyboard shortcuts defined in the config.h file. 67 | 68 | * The window *W* at the top of the stack can be resized on a per desktop basis. 69 | * Changing a tiling mode or window size on one desktop doesn't affect the other desktops. 70 | 71 | 72 | ###Recent Changes 73 | -------------- 74 | 75 | 9/8/12 76 | 77 | > In horizontal or vertical tiling modes windows can be added to the master area with keyboard shortcuts 78 | 79 | ###Status 80 | ------ 81 | 82 | There are more options in the config file than the original catwm. 83 | 84 | * Fixed the window manager crashing on a bad window error. 85 | * Fixed the keyboard shortcuts not working if numlock was on. 86 | * Added some functions. 87 | * Added an option to focus the window the mouse just moved to. 88 | * Fixed a window being destroyed on another desktop creating ghost windows. 89 | * Added ability to resize the window on the top of the stack 90 | * Added having applications open on specified desktop 91 | * Added a click to focus option 92 | * Added ability to change back to last opened desktop. 93 | * Transient windows are now always floating and always on top 94 | * Option in the config file to not show a panel when a window is open. 95 | * Last focused window is refocused and the mouse moved to it on desktop change 96 | * Added option in the config to have new window opened top or bottom of the stack when using atach aside 97 | * Added stacking mode 98 | * Changed grid mode so first column and second window are resizable 99 | 100 | 101 | ###Installation 102 | ------------ 103 | 104 | Need Xlib, then: 105 | 106 | edit the config.h.def file to suit your needs 107 | and save it as config.h. 108 | 109 | $ make 110 | # make install 111 | $ make clean 112 | 113 | 114 | ###Bugs 115 | ---- 116 | 117 | [ * No bugs for the moment ;) (I mean, no importants bugs ;)] 118 | 119 | 120 | ###Todo 121 | ---- 122 | 123 | * 124 | 125 | -------------------------------------------------------------------------------- /config.h.def: -------------------------------------------------------------------------------- 1 | /* config.h for dminiwm.c [ 0.4.5 ] */ 2 | 3 | #ifndef CONFIG_H 4 | #define CONFIG_H 5 | 6 | #define DESKTOPS 6 /* Must edit DESKTOPCHANGE keys to suit */ 7 | #define MOD1 Mod1Mask 8 | #define MOD4 Mod4Mask 9 | #define OUTPUT_INFO 0 /* 1=Don't 0=Output info for bipolarbar */ 10 | #define MASTER_SIZE 0.52 11 | #define RESIZEMOVEKEY MOD1 12 | #define TOP_PANEL 0 /* 1=Don't 0=Have the panel at the top instead of the bottom */ 13 | #define PANEL_HEIGHT 18 /* 0 for no space for a panel */ 14 | #define SHOW_BAR 0 /* 1=Don't 0=Have the bar shown with a window open */ 15 | #define BORDER_WIDTH 2 16 | #define ATTACH_ASIDE 1 /* 0=TRUE, 1=New window is master */ 17 | #define TOP_STACK 1 /* 0=TRUE, 1=New window added to bottom of stack when using ATTACH_ASIDE */ 18 | #define DEFAULT_MODE 1 /* 1=Vertical 2=Fullscreen 3=Horizontal 4=grid 5=stacking */ 19 | /* Set mode for each desktop - defaults to DEFAULT_MODE */ 20 | const unsigned int MODES[] = {1, 2, 3, 4, 5}; 21 | /* Only one of FOLLOW_MOUSE and CLICK_TO_FOCUS should be zero */ 22 | #define FOLLOW_MOUSE 1 /* 1=Don't 0=Focus the window the mouse just entered */ 23 | #define CLICK_TO_FOCUS 0 /* 1=Don't 0=Focus an unfocused window when clicked */ 24 | 25 | /* Colors */ 26 | #define FOCUS "#664422" // dkorange 27 | #define UNFOCUS "#004050" // blueish 28 | 29 | static const Convenience convenience[] = { \ 30 | /* class desktop follow */ 31 | /*{ "Thunar", 2, 1 }, */ 32 | }; 33 | 34 | static const Positional positional[] = { \ 35 | /* class x y width height */ 36 | /*{ "Thunar", 100,100,800,400 }, */ 37 | }; 38 | 39 | const char* dmenucmd[] = {"dmenu_run","-i","-nb","#666622","-nf","white",NULL}; 40 | const char* urxvtcmd[] = {"urxvtc",NULL}; 41 | const char* thunarcmd[] = {"thunar",NULL}; 42 | 43 | #define DESKTOPCHANGE(K,N) \ 44 | { MOD1, K, change_desktop, {.i = N}}, \ 45 | { MOD1|ShiftMask, K, follow_client_to_desktop, {.i = N}}, \ 46 | { MOD4|ShiftMask, K, client_to_desktop, {.i = N}}, 47 | 48 | static key keys[] = { 49 | /* MOD KEY FUNCTION ARGS */ 50 | { MOD1, XK_h, resize_master, {.i = 10}}, 51 | { MOD1, XK_l, resize_master, {.i = -10}}, 52 | { MOD1, XK_c, kill_client, {NULL}}, 53 | { MOD1, XK_j, next_win, {NULL}}, 54 | { MOD1, XK_k, prev_win, {NULL}}, 55 | { MOD1, XK_v, spawn, {.com = dmenucmd}}, 56 | { MOD1, XK_p, resize_stack, {.i = +10}}, 57 | { MOD1, XK_o, resize_stack, {.i = -10}}, 58 | { MOD1, XK_Tab, last_desktop, {NULL}}, 59 | { MOD1, XK_b, toggle_panel, {NULL}}, 60 | { MOD1, XK_Return, spawn, {.com = urxvtcmd}}, 61 | { MOD1, XK_a, rotate_mode, {.i = 1}}, 62 | { MOD1|ShiftMask, XK_j, move_up, {.i = -10}}, 63 | { MOD1|ShiftMask, XK_k, move_down, {.i = 10}}, 64 | { MOD1|ShiftMask, XK_o, move_sideways, {.i = -10}},//left 65 | { MOD1|ShiftMask, XK_p, move_sideways, {.i = 10}},//right 66 | { MOD1|ShiftMask, XK_Return, swap_master, {NULL}}, 67 | { MOD1|ShiftMask, XK_g, switch_mode, {.i = 3}}, // grid 68 | { MOD1|ShiftMask, XK_h, switch_mode, {.i = 2}}, // horizontal 69 | { MOD1|ShiftMask, XK_f, switch_mode, {.i = 1}}, // fullscreen 70 | { MOD1|ShiftMask, XK_v, switch_mode, {.i = 0}}, // vertical 71 | { MOD1|ShiftMask, XK_c, switch_mode, {.i = 4}}, // stacking 72 | { MOD1|ShiftMask, XK_m, more_master, {.i = 1}}, // add window to master area 73 | { MOD1|ShiftMask, XK_l, more_master, {.i = -1}}, // remove window from master area 74 | { MOD1|ControlMask, XK_q, quit, {NULL}}, 75 | { MOD4, XK_Right, rotate_desktop, {.i = 1}}, 76 | { MOD4, XK_Left, rotate_desktop, {.i = -1}}, 77 | { MOD4, XK_h, spawn, {.com = thunarcmd}}, 78 | DESKTOPCHANGE( XK_1, 0) 79 | DESKTOPCHANGE( XK_2, 1) 80 | DESKTOPCHANGE( XK_3, 2) 81 | DESKTOPCHANGE( XK_4, 3) 82 | DESKTOPCHANGE( XK_5, 4) 83 | DESKTOPCHANGE( XK_6, 5) 84 | }; 85 | 86 | #endif 87 | 88 | -------------------------------------------------------------------------------- /dminiwm.c: -------------------------------------------------------------------------------- 1 | /* dminiwm.c [ 0.4.5 ] 2 | * 3 | * I started this from catwm 31/12/10 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #include 25 | #include 26 | //#include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 39 | #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) 40 | #define TABLENGTH(X) (sizeof(X)/sizeof(*X)) 41 | 42 | typedef union { 43 | const char** com; 44 | const int i; 45 | } Arg; 46 | 47 | // Structs 48 | typedef struct { 49 | unsigned int mod; 50 | KeySym keysym; 51 | void (*function)(const Arg arg); 52 | const Arg arg; 53 | } key; 54 | 55 | typedef struct client client; 56 | struct client{ 57 | // Prev and next client 58 | client *next, *prev; 59 | // The window 60 | Window win; 61 | unsigned int x, y, width, height, order; 62 | }; 63 | 64 | typedef struct desktop desktop; 65 | struct desktop{ 66 | unsigned int master_size, mode, growth, numwins, nmaster, showbar; 67 | client *head,*current, *transient; 68 | }; 69 | 70 | typedef struct { 71 | const char *class; 72 | unsigned int preferredd, followwin; 73 | } Convenience; 74 | 75 | typedef struct { 76 | const char *class; 77 | unsigned int x, y, width, height; 78 | } Positional; 79 | 80 | // Functions 81 | static void add_window(Window w, unsigned int tw, client *cl); 82 | static void buttonpress(XEvent *e); 83 | static void buttonrelease(XEvent *e); 84 | static void change_desktop(const Arg arg); 85 | static void client_to_desktop(const Arg arg); 86 | static void configurerequest(XEvent *e); 87 | static void destroynotify(XEvent *e); 88 | static void follow_client_to_desktop(const Arg arg); 89 | static unsigned long getcolor(const char* color); 90 | static void grabkeys(); 91 | static void keypress(XEvent *e); 92 | static void kill_client(); 93 | static void kill_client_now(Window w); 94 | static void last_desktop(); 95 | static void logger(const char* e); 96 | static void maprequest(XEvent *e); 97 | static void motionnotify(XEvent *e); 98 | static void more_master(const Arg arg); 99 | static void move_down(const Arg arg); 100 | static void move_up(const Arg arg); 101 | static void move_sideways(const Arg arg); 102 | static void next_win(); 103 | static void prev_win(); 104 | static void quit(); 105 | static void remove_window(Window w, unsigned int dr, unsigned int tw); 106 | static void resize_master(const Arg arg); 107 | static void resize_stack(const Arg arg); 108 | static void rotate_desktop(const Arg arg); 109 | static void rotate_mode(const Arg arg); 110 | static void save_desktop(unsigned int i); 111 | static void select_desktop(unsigned int i); 112 | static void setup(); 113 | static void sigchld(int unused); 114 | static void spawn(const Arg arg); 115 | static void start(); 116 | static void swap_master(); 117 | static void switch_mode(const Arg arg); 118 | static void tile(); 119 | static void toggle_panel(); 120 | static void unmapnotify(XEvent *e); // Thunderbird's write window just unmaps... 121 | static void update_current(); 122 | static void update_info(); 123 | static void warp_pointer(); 124 | 125 | // Include configuration file (need struct key) 126 | #include "config.h" 127 | 128 | // Variable 129 | static Display *dis; 130 | static unsigned int bool_quit, current_desktop, previous_desktop, doresize; 131 | static int growth, sh, sw, master_size, nmaster; 132 | static unsigned int mode, panel_size, showbar, screen, bdw, numwins, win_focus, win_unfocus; 133 | static int xerror(Display *dis, XErrorEvent *ee), (*xerrorxlib)(Display *, XErrorEvent *); 134 | unsigned int numlockmask; /* dynamic key lock mask */ 135 | static Window root; 136 | static client *head, *current, *transient; 137 | static XWindowAttributes attr; 138 | static XButtonEvent starter; 139 | static Atom *protocols, wm_delete_window, protos; 140 | 141 | // Events array 142 | static void (*events[LASTEvent])(XEvent *e) = { 143 | [KeyPress] = keypress, 144 | [MapRequest] = maprequest, 145 | [UnmapNotify] = unmapnotify, 146 | [ButtonPress] = buttonpress, 147 | [MotionNotify] = motionnotify, 148 | [ButtonRelease] = buttonrelease, 149 | [DestroyNotify] = destroynotify, 150 | [ConfigureRequest] = configurerequest 151 | }; 152 | 153 | // Desktop array 154 | static desktop desktops[DESKTOPS]; 155 | 156 | /* ***************************** Window Management ******************************* */ 157 | void add_window(Window w, unsigned int tw, client *cl) { 158 | client *c,*t, *dummy = head; 159 | 160 | if(cl != NULL) c = cl; 161 | else if(!(c = (client *)calloc(1,sizeof(client)))) { 162 | logger("\033[0;31mError calloc!"); 163 | exit(1); 164 | } 165 | 166 | if(tw == 0 && cl == NULL) { 167 | XClassHint chh = {0}; 168 | unsigned int i, j=0; 169 | if(XGetClassHint(dis, w, &chh)) { 170 | for(i=0;ix = attr.x; 185 | if(TOP_PANEL == 0 && attr.y < panel_size) c->y = panel_size; 186 | else c->y = attr.y; 187 | c->width = attr.width; 188 | c->height = attr.height; 189 | } 190 | 191 | c->win = w; c->order = 0; 192 | if(tw == 1) dummy = transient; // For the transient window 193 | for(t=dummy;t;t=t->next) 194 | ++t->order; 195 | 196 | if(dummy == NULL) { 197 | c->next = NULL; c->prev = NULL; 198 | dummy = c; 199 | } else { 200 | if(ATTACH_ASIDE == 0) { 201 | if(TOP_STACK == 0) { 202 | c->next = dummy->next; c->prev = dummy; 203 | dummy->next = c; 204 | } else { 205 | for(t=dummy;t->next;t=t->next); // Start at the last in the stack 206 | t->next = c; c->next = NULL; 207 | c->prev = t; 208 | } 209 | } else { 210 | c->prev = NULL; c->next = dummy; 211 | c->next->prev = c; 212 | dummy = c; 213 | } 214 | } 215 | 216 | if(tw == 1) { 217 | transient = dummy; 218 | save_desktop(current_desktop); 219 | return; 220 | } else head = dummy; 221 | current = c; 222 | numwins += 1; 223 | growth = (growth > 0) ? growth*(numwins-1)/numwins:0; 224 | save_desktop(current_desktop); 225 | // for folow mouse 226 | if(FOLLOW_MOUSE == 0) XSelectInput(dis, c->win, PointerMotionMask); 227 | } 228 | 229 | void remove_window(Window w, unsigned int dr, unsigned int tw) { 230 | client *c, *t, *dummy; 231 | 232 | dummy = (tw == 1) ? transient : head; 233 | for(c=dummy;c;c=c->next) { 234 | if(c->win == w) { 235 | if(c->prev == NULL && c->next == NULL) { 236 | dummy = NULL; 237 | } else if(c->prev == NULL) { 238 | dummy = c->next; 239 | c->next->prev = NULL; 240 | } else if(c->next == NULL) { 241 | c->prev->next = NULL; 242 | } else { 243 | c->prev->next = c->next; 244 | c->next->prev = c->prev; 245 | } break; 246 | } 247 | } 248 | if(tw == 1) { 249 | transient = dummy; 250 | free(c); 251 | save_desktop(current_desktop); 252 | update_current(); 253 | return; 254 | } else { 255 | head = dummy; 256 | XUngrabButton(dis, AnyButton, AnyModifier, c->win); 257 | XUnmapWindow(dis, c->win); 258 | numwins -= 1; 259 | if(head != NULL) { 260 | for(t=head;t;t=t->next) { 261 | if(t->order > c->order) --t->order; 262 | if(t->order == 0) current = t; 263 | } 264 | } else current = NULL; 265 | if(dr == 0) free(c); 266 | if(numwins < 3) growth = 0; 267 | if(nmaster > 0 && nmaster == (numwins-1)) nmaster -= 1; 268 | save_desktop(current_desktop); 269 | if(mode != 4) tile(); 270 | update_current(); 271 | return; 272 | } 273 | } 274 | 275 | void next_win() { 276 | if(numwins < 2) return; 277 | current = (current->next == NULL) ? head:current->next; 278 | if(mode == 1) tile(); 279 | update_current(); 280 | } 281 | 282 | void prev_win() { 283 | if(numwins < 2) return; 284 | client *c; 285 | 286 | if(current->prev == NULL) for(c=head;c->next;c=c->next); 287 | else c = current->prev; 288 | 289 | current = c; 290 | if(mode == 1) tile(); 291 | update_current(); 292 | } 293 | 294 | void move_down(const Arg arg) { 295 | if(mode == 4 && current != NULL) { 296 | current->y += arg.i; 297 | XMoveResizeWindow(dis,current->win,current->x,current->y,current->width,current->height); 298 | return; 299 | } 300 | if(current == NULL || current->next == NULL || current->win == head->win || current->prev == NULL) 301 | return; 302 | Window tmp = current->win; 303 | current->win = current->next->win; 304 | current->next->win = tmp; 305 | //keep the moved window activated 306 | next_win(); 307 | save_desktop(current_desktop); 308 | tile(); 309 | } 310 | 311 | void move_up(const Arg arg) { 312 | if(mode == 4 && current != NULL) { 313 | current->y += arg.i; 314 | XMoveResizeWindow(dis,current->win,current->x,current->y,current->width,current->height); 315 | return; 316 | } 317 | if(current == NULL || current->prev == head || current->win == head->win) 318 | return; 319 | Window tmp = current->win; 320 | current->win = current->prev->win; 321 | current->prev->win = tmp; 322 | prev_win(); 323 | save_desktop(current_desktop); 324 | tile(); 325 | } 326 | 327 | void move_sideways(const Arg arg) { 328 | if(mode == 4 && current != NULL) { 329 | current->x += arg.i; 330 | XMoveResizeWindow(dis,current->win,current->x,current->y,current->width,current->height); 331 | } 332 | } 333 | 334 | void swap_master() { 335 | if(numwins < 2 || mode == 1 || mode == 4) return; 336 | Window tmp; 337 | 338 | if(current == head) { 339 | tmp = head->next->win; head->next->win = head->win; 340 | head->win = tmp; 341 | } else { 342 | tmp = head->win; head->win = current->win; 343 | current->win = tmp; current = head; 344 | } 345 | save_desktop(current_desktop); 346 | tile(); 347 | update_current(); 348 | } 349 | 350 | /* **************************** Desktop Management ************************************* */ 351 | void update_info() { 352 | if(OUTPUT_INFO != 0) return; 353 | unsigned int i, j; 354 | 355 | fflush(stdout); 356 | for(i=0;i 0 && showbar == 1) || (panel_size < 1 && showbar == 0)) toggle_panel(); 375 | // Map all windows 376 | if(head != NULL) { 377 | if(mode != 1) 378 | for(c=head;c;c=c->next) 379 | XMapWindow(dis,c->win); 380 | tile(); 381 | } 382 | if(transient != NULL) 383 | for(c=transient;c;c=c->next) 384 | XMapWindow(dis,c->win); 385 | 386 | select_desktop(tmp); 387 | // Unmap all window 388 | if(transient != NULL) 389 | for(c=transient;c;c=c->next) 390 | XUnmapWindow(dis,c->win); 391 | if(head != NULL) 392 | for(c=head;c;c=c->next) 393 | XUnmapWindow(dis,c->win); 394 | 395 | select_desktop(arg.i); 396 | update_current(); 397 | update_info(); 398 | } 399 | 400 | void last_desktop() { 401 | Arg a = {.i = previous_desktop}; 402 | change_desktop(a); 403 | } 404 | 405 | void rotate_desktop(const Arg arg) { 406 | Arg a = {.i = (current_desktop + DESKTOPS + arg.i) % DESKTOPS}; 407 | change_desktop(a); 408 | } 409 | 410 | void rotate_mode(const Arg arg) { 411 | // there's five hardcoded modes so use 5 in the math 412 | Arg a = {.i = (mode + 5 + arg.i) % 5}; 413 | switch_mode(a); 414 | } 415 | 416 | void follow_client_to_desktop(const Arg arg) { 417 | client_to_desktop(arg); 418 | change_desktop(arg); 419 | } 420 | 421 | void client_to_desktop(const Arg arg) { 422 | if(arg.i == current_desktop || current == NULL) return; 423 | 424 | client *tmp = current; 425 | unsigned int tmp2 = current_desktop; 426 | 427 | // Remove client from current desktop 428 | remove_window(current->win, 1, 0); 429 | 430 | // Add client to desktop 431 | select_desktop(arg.i); 432 | add_window(tmp->win, 0, tmp); 433 | save_desktop(arg.i); 434 | select_desktop(tmp2); 435 | update_info(); 436 | } 437 | 438 | void save_desktop(unsigned int i) { 439 | desktops[i].master_size = master_size; 440 | desktops[i].nmaster = nmaster; 441 | desktops[i].numwins = numwins; 442 | desktops[i].mode = mode; 443 | desktops[i].growth = growth; 444 | desktops[i].showbar = showbar; 445 | desktops[i].head = head; 446 | desktops[i].current = current; 447 | desktops[i].transient = transient; 448 | } 449 | 450 | void select_desktop(unsigned int i) { 451 | master_size = desktops[i].master_size; 452 | nmaster = desktops[i].nmaster; 453 | numwins = desktops[i].numwins; 454 | mode = desktops[i].mode; 455 | growth = desktops[i].growth; 456 | showbar = desktops[i].showbar; 457 | head = desktops[i].head; 458 | current = desktops[i].current; 459 | transient = desktops[i].transient; 460 | current_desktop = i; 461 | } 462 | 463 | void more_master (const Arg arg) { 464 | if(arg.i > 0) { 465 | if((numwins < 3) || (nmaster == (numwins-2))) return; 466 | nmaster += 1; 467 | } else { 468 | if(nmaster == 0) return; 469 | nmaster -= 1; 470 | } 471 | save_desktop(current_desktop); 472 | tile(); 473 | } 474 | 475 | void tile() { 476 | if(head == NULL) return; 477 | client *c, *d = NULL; 478 | unsigned int x = 0, xpos = 0, ypos, wdt = 0, msw, ssw, ncols = 2, nrows = 1; 479 | int ht = 0, y = 0, n = 0; 480 | 481 | // For a top panel 482 | if(TOP_PANEL == 0) y = panel_size; ypos = y; 483 | 484 | // If only one window 485 | if(mode != 4 && head != NULL && head->next == NULL) { 486 | if(mode == 1) XMapWindow(dis, current->win); 487 | XMoveResizeWindow(dis,head->win,0,y,sw+bdw,sh+bdw); 488 | } else { 489 | switch(mode) { 490 | case 0: /* Vertical */ 491 | // Master window 492 | if(nmaster < 1) 493 | XMoveResizeWindow(dis,head->win,0,y,master_size - bdw,sh - bdw); 494 | else { 495 | for(d=head;d;d=d->next) { 496 | XMoveResizeWindow(dis,d->win,0,ypos,master_size - bdw,sh/(nmaster+1) - bdw); 497 | if(x == nmaster) break; 498 | ypos += sh/(nmaster+1); ++x; 499 | } 500 | } 501 | 502 | // Stack 503 | if(d == NULL) d = head; 504 | n = numwins - (nmaster+1); 505 | XMoveResizeWindow(dis,d->next->win,master_size,y,sw-master_size-bdw,(sh/n)+growth - bdw); 506 | y += (sh/n)+growth; 507 | for(c=d->next->next;c;c=c->next) { 508 | XMoveResizeWindow(dis,c->win,master_size,y,sw-master_size-bdw,(sh/n)-(growth/(n-1)) - bdw); 509 | y += (sh/n)-(growth/(n-1)); 510 | } 511 | break; 512 | case 1: /* Fullscreen */ 513 | XMoveResizeWindow(dis,current->win,0,y,sw+bdw,sh+bdw); 514 | XMapWindow(dis, current->win); 515 | break; 516 | case 2: /* Horizontal */ 517 | // Master window 518 | if(nmaster < 1) 519 | XMoveResizeWindow(dis,head->win,xpos,ypos,sw-bdw,master_size-bdw); 520 | else { 521 | for(d=head;d;d=d->next) { 522 | XMoveResizeWindow(dis,d->win,xpos,ypos,sw/(nmaster+1)-bdw,master_size-bdw); 523 | if(x == nmaster) break; 524 | xpos += sw/(nmaster+1); ++x; 525 | } 526 | } 527 | 528 | // Stack 529 | if(d == NULL) d = head; 530 | n = numwins - (nmaster+1); 531 | XMoveResizeWindow(dis,d->next->win,0,y+master_size,(sw/n)+growth-bdw,sh-master_size-bdw); 532 | msw = (sw/n)+growth; 533 | for(c=d->next->next;c;c=c->next) { 534 | XMoveResizeWindow(dis,c->win,msw,y+master_size,(sw/n)-(growth/(n-1)) - bdw,sh-master_size-bdw); 535 | msw += (sw/n)-(growth/(n-1)); 536 | } 537 | break; 538 | case 3: // Grid 539 | x = numwins; 540 | for(xpos=0;xpos<=x;++xpos) { 541 | if(xpos == 3 || xpos == 7 || xpos == 10 || xpos == 17) ++nrows; 542 | if(xpos == 5 || xpos == 13 || xpos == 21) ++ncols; 543 | } 544 | msw = (ncols > 2) ? ((master_size*2)/ncols) : master_size; 545 | ssw = (sw - msw)/(ncols-1); ht = sh/nrows; 546 | xpos = msw+(ssw*(ncols-2)); ypos = y+((nrows-1)*ht); 547 | for(c=head;c->next;c=c->next); 548 | for(d=c;d;d=d->prev) { 549 | --x; 550 | if(n == nrows) { 551 | xpos -= (xpos == msw) ? msw : ssw; 552 | ypos = y+((nrows-1)*ht); 553 | n = 0; 554 | } 555 | if(x == 0) { 556 | ht = (ypos-y+ht); 557 | ypos = y; 558 | } 559 | if(x == 2 && xpos == msw && ypos != y) { 560 | ht -= growth; 561 | ypos += growth; 562 | } 563 | if(x == 1) { 564 | ht += growth; 565 | ypos -= growth; 566 | } 567 | wdt = (xpos > 0) ? ssw : msw; 568 | XMoveResizeWindow(dis,d->win,xpos,ypos,wdt-bdw,ht-bdw); 569 | ht = sh/nrows; 570 | ypos -= ht; ++n; 571 | } 572 | break; 573 | case 4: // Stacking 574 | for(c=head;c;c=c->next) 575 | XMoveResizeWindow(dis,c->win,c->x,c->y,c->width,c->height); 576 | break; 577 | } 578 | } 579 | } 580 | 581 | void update_current() { 582 | if(head == NULL) return; 583 | client *c, *d; unsigned int border; 584 | 585 | border = ((head->next == NULL && mode != 4) || (mode == 1)) ? 0 : bdw; 586 | for(c=head;c->next;c=c->next); 587 | for(d=c;d;d=d->prev) { 588 | XSetWindowBorderWidth(dis,d->win,border); 589 | 590 | if(d != current) { 591 | if(d->order < current->order) ++d->order; 592 | XSetWindowBorder(dis,d->win,win_unfocus); 593 | if(CLICK_TO_FOCUS == 0) 594 | XGrabButton(dis, AnyButton, AnyModifier, d->win, True, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); 595 | } 596 | else { 597 | // "Enable" current window 598 | XSetWindowBorder(dis,d->win,win_focus); 599 | XSetInputFocus(dis,d->win,RevertToParent,CurrentTime); 600 | XRaiseWindow(dis,d->win); 601 | if(CLICK_TO_FOCUS == 0) 602 | XUngrabButton(dis, AnyButton, AnyModifier, d->win); 603 | } 604 | } 605 | current->order = 0; 606 | if(transient != NULL) { 607 | for(c=transient;c->next;c=c->next); 608 | for(d=c;d;d=d->prev) 609 | XRaiseWindow(dis,d->win); 610 | XSetInputFocus(dis,transient->win,RevertToParent,CurrentTime); 611 | } 612 | warp_pointer(); 613 | XSync(dis, False); 614 | } 615 | 616 | void switch_mode(const Arg arg) { 617 | if(mode == arg.i) return; 618 | client *c; 619 | 620 | growth = 0; 621 | if(mode == 1 && current != NULL && head->next != NULL) { 622 | XUnmapWindow(dis, current->win); 623 | for(c=head;c;c=c->next) 624 | XMapWindow(dis, c->win); 625 | } 626 | 627 | mode = arg.i; 628 | master_size = (mode == 2) ? sh*MASTER_SIZE : sw*MASTER_SIZE; 629 | if(mode == 1 && current != NULL && head->next != NULL) 630 | for(c=head;c;c=c->next) 631 | XUnmapWindow(dis, c->win); 632 | 633 | tile(); 634 | update_current(); 635 | update_info(); 636 | } 637 | 638 | void resize_master(const Arg arg) { 639 | if(mode == 4 && current != NULL) { 640 | current->width += arg.i; 641 | XMoveResizeWindow(dis,current->win,current->x,current->y,current->width,current->height); 642 | } else if(arg.i > 0) { 643 | if((mode != 2 && sw-master_size > 70) || (mode == 2 && sh-master_size > 70)) 644 | master_size += arg.i; 645 | } else if(master_size > 70) master_size += arg.i; 646 | tile(); 647 | } 648 | 649 | void resize_stack(const Arg arg) { 650 | if(mode == 4 && current != NULL) { 651 | current->height += arg.i; 652 | XMoveResizeWindow(dis,current->win,current->x,current->y,current->width,current->height+arg.i); 653 | } else if(nmaster == (numwins-2)) return; 654 | else if(mode == 3) { 655 | if(arg.i > 0 && ((sh/2+growth) < (sh-100))) growth += arg.i; 656 | else if(arg.i < 0 && ((sh/2+growth) > 80)) growth += arg.i; 657 | tile(); 658 | } else if(numwins > 2) { 659 | unsigned int n = numwins-1; 660 | if(arg.i >0) { 661 | if((mode != 2 && sh-(growth+sh/n) > (n-1)*70) || (mode == 2 && sw-(growth+sw/n) > (n-1)*70)) 662 | growth += arg.i; 663 | } else { 664 | if((mode != 2 && (sh/n+growth) > 70) || (mode == 2 && (sw/n+growth) > 70)) 665 | growth += arg.i; 666 | } 667 | tile(); 668 | } 669 | } 670 | 671 | void toggle_panel() { 672 | if(PANEL_HEIGHT > 0) { 673 | if(panel_size >0) { 674 | sh += panel_size; 675 | panel_size = 0; 676 | showbar = 1; 677 | } else { 678 | panel_size = PANEL_HEIGHT; 679 | sh -= panel_size; 680 | showbar = 0; 681 | } 682 | tile(); 683 | } 684 | } 685 | 686 | /* ********************** Keyboard Management ********************** */ 687 | void grabkeys() { 688 | unsigned int i,j; 689 | KeyCode code; 690 | 691 | // numlock workaround 692 | XModifierKeymap *modmap; 693 | numlockmask = 0; 694 | modmap = XGetModifierMapping(dis); 695 | for (i=0;i<8;++i) { 696 | for (j=0;jmax_keypermod;++j) { 697 | if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dis, XK_Num_Lock)) 698 | numlockmask = (1 << i); 699 | } 700 | } 701 | XFreeModifiermap(modmap); 702 | 703 | XUngrabKey(dis, AnyKey, AnyModifier, root); 704 | // For each shortcuts 705 | for(i=0;ixkey; 724 | 725 | keysym = XkbKeycodeToKeysym(dis, (KeyCode)ev->keycode, 0, 0); 726 | //fprintf(stderr, "pressed key %s\n", XKeysymToString(keysym)); 727 | for(i=0;istate)) { 729 | if(keys[i].function) 730 | keys[i].function(keys[i].arg); 731 | } 732 | } 733 | } 734 | 735 | void warp_pointer() { 736 | // Move cursor to the center of the current window 737 | if(FOLLOW_MOUSE == 0 && head != NULL) { 738 | XGetWindowAttributes(dis, current->win, &attr); 739 | XWarpPointer(dis, None, current->win, 0, 0, 0, 0, attr.width/2, attr.height/2); 740 | } 741 | } 742 | 743 | /* ********************** Signal Management ************************** */ 744 | void configurerequest(XEvent *e) { 745 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 746 | XWindowChanges wc; 747 | unsigned int y = (TOP_PANEL == 0) ? panel_size:0; 748 | 749 | wc.x = ev->x; 750 | wc.y = ev->y + y; 751 | wc.width = (ev->width < sw-bdw) ? ev->width:sw+bdw; 752 | wc.height = (ev->height < sh-bdw-y) ? ev->height:sh+bdw; 753 | wc.border_width = 0; 754 | wc.sibling = ev->above; 755 | wc.stack_mode = ev->detail; 756 | XConfigureWindow(dis, ev->window, ev->value_mask, &wc); 757 | XSync(dis, False); 758 | } 759 | 760 | void maprequest(XEvent *e) { 761 | XMapRequestEvent *ev = &e->xmaprequest; 762 | 763 | XGetWindowAttributes(dis, ev->window, &attr); 764 | if(attr.override_redirect) return; 765 | 766 | unsigned int y=0; 767 | if(TOP_PANEL == 0) y = panel_size; 768 | // For fullscreen mplayer (and maybe some other program) 769 | client *c; 770 | 771 | for(c=head;c;c=c->next) 772 | if(ev->window == c->win) { 773 | XMapWindow(dis,ev->window); 774 | return; 775 | } 776 | 777 | Window trans = None; 778 | if (XGetTransientForHint(dis, ev->window, &trans) && trans != None) { 779 | add_window(ev->window, 1, NULL); 780 | if((attr.y + attr.height) > sh) 781 | XMoveResizeWindow(dis,ev->window,attr.x,y,attr.width,attr.height-10); 782 | XSetWindowBorderWidth(dis,ev->window,bdw); 783 | XSetWindowBorder(dis,ev->window,win_focus); 784 | XMapWindow(dis, ev->window); 785 | update_current(); 786 | return; 787 | } 788 | 789 | if(mode == 1 && current != NULL) XUnmapWindow(dis, current->win); 790 | XClassHint ch = {0}; 791 | unsigned int i=0, j=0, tmp = current_desktop; 792 | if(XGetClassHint(dis, ev->window, &ch)) 793 | for(i=0;inext) 799 | if(ev->window == c->win) 800 | ++j; 801 | if(j < 1) add_window(ev->window, 0, NULL); 802 | if(tmp == convenience[i].preferredd-1) { 803 | tile(); 804 | XMapWindow(dis, ev->window); 805 | update_current(); 806 | } else select_desktop(tmp); 807 | if(convenience[i].followwin != 0) { 808 | Arg a = {.i = convenience[i].preferredd-1}; 809 | change_desktop(a); 810 | } 811 | if(ch.res_class) XFree(ch.res_class); 812 | if(ch.res_name) XFree(ch.res_name); 813 | update_info(); 814 | return; 815 | } 816 | if(ch.res_class) XFree(ch.res_class); 817 | if(ch.res_name) XFree(ch.res_name); 818 | 819 | add_window(ev->window, 0, NULL); 820 | if(mode != 4) tile(); 821 | if(mode != 1) XMapWindow(dis,ev->window); 822 | update_current(); 823 | update_info(); 824 | } 825 | 826 | void destroynotify(XEvent *e) { 827 | unsigned int i = 0, tmp = current_desktop; 828 | client *c; 829 | XDestroyWindowEvent *ev = &e->xdestroywindow; 830 | 831 | save_desktop(tmp); 832 | for(i=current_desktop;inext) 835 | if(ev->window == c->win) { 836 | remove_window(ev->window, 0, 0); 837 | select_desktop(tmp); 838 | update_info(); 839 | return; 840 | } 841 | if(transient != NULL) { 842 | for(c=transient;c;c=c->next) 843 | if(ev->window == c->win) { 844 | remove_window(ev->window, 0, 1); 845 | select_desktop(tmp); 846 | return; 847 | } 848 | } 849 | } 850 | select_desktop(tmp); 851 | } 852 | 853 | void unmapnotify(XEvent *e) { // for thunderbird's write window and maybe others 854 | XUnmapEvent *ev = &e->xunmap; 855 | client *c; 856 | 857 | if(ev->send_event == 1) { 858 | for(c=head;c;c=c->next) 859 | if(ev->window == c->win) { 860 | remove_window(ev->window, 1, 0); 861 | update_info(); 862 | return; 863 | } 864 | } 865 | } 866 | 867 | void buttonpress(XEvent *e) { 868 | XButtonEvent *ev = &e->xbutton; 869 | client *c; 870 | 871 | for(c=transient;c;c=c->next) 872 | if(ev->window == c->win) { 873 | XSetInputFocus(dis,ev->window,RevertToParent,CurrentTime); 874 | return; 875 | } 876 | // change focus with LMB 877 | if(CLICK_TO_FOCUS == 0 && ev->window != current->win && ev->button == Button1) 878 | for(c=head;c;c=c->next) 879 | if(ev->window == c->win) { 880 | current = c; 881 | update_current(); 882 | XSendEvent(dis, PointerWindow, False, 0xfff, e); 883 | XFlush(dis); 884 | return; 885 | } 886 | 887 | if(ev->subwindow == None || mode != 4) return; 888 | for(c=head;c;c=c->next) 889 | if(ev->subwindow == c->win) { 890 | XGrabPointer(dis, ev->subwindow, True, PointerMotionMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); 891 | XGetWindowAttributes(dis, ev->subwindow, &attr); 892 | starter = e->xbutton; doresize = 1; 893 | } 894 | } 895 | 896 | void motionnotify(XEvent *e) { 897 | int xdiff, ydiff; 898 | client *c; 899 | XMotionEvent *ev = &e->xmotion; 900 | 901 | if(ev->window != current->win) { 902 | for(c=head;c;c=c->next) 903 | if(ev->window == c->win) { 904 | current = c; 905 | update_current(); 906 | return; 907 | } 908 | } 909 | if(doresize < 1) return; 910 | while(XCheckTypedEvent(dis, MotionNotify, e)); 911 | xdiff = ev->x_root - starter.x_root; 912 | ydiff = ev->y_root - starter.y_root; 913 | XMoveResizeWindow(dis, ev->window, attr.x + (starter.button==1 ? xdiff : 0), attr.y + (starter.button==1 ? ydiff : 0), 914 | MAX(1, attr.width + (starter.button==3 ? xdiff : 0)), 915 | MAX(1, attr.height + (starter.button==3 ? ydiff : 0))); 916 | } 917 | 918 | void buttonrelease(XEvent *e) { 919 | XButtonEvent *ev = &e->xbutton; 920 | 921 | if(doresize < 1) { 922 | XSendEvent(dis, PointerWindow, False, 0xfff, e); 923 | XFlush(dis); 924 | return; 925 | } 926 | XUngrabPointer(dis, CurrentTime); 927 | if(mode != 4) return; 928 | if(ev->window == current->win) { 929 | XGetWindowAttributes(dis, current->win, &attr); 930 | current->x = attr.x; 931 | current->y = attr.y; 932 | current->width = attr.width; 933 | current->height = attr.height; 934 | } 935 | update_current(); 936 | doresize = 0; 937 | } 938 | 939 | void kill_client() { 940 | if(head == NULL) return; 941 | kill_client_now(current->win); 942 | remove_window(current->win, 0, 0); 943 | update_info(); 944 | } 945 | 946 | void kill_client_now(Window w) { 947 | int n, i; 948 | XEvent ke; 949 | 950 | if (XGetWMProtocols(dis, w, &protocols, &n) != 0) { 951 | for(i=n;i>=0;--i) { 952 | if (protocols[i] == wm_delete_window) { 953 | ke.type = ClientMessage; 954 | ke.xclient.window = w; 955 | ke.xclient.message_type = protos; 956 | ke.xclient.format = 32; 957 | ke.xclient.data.l[0] = wm_delete_window; 958 | ke.xclient.data.l[1] = CurrentTime; 959 | XSendEvent(dis, w, False, NoEventMask, &ke); 960 | } 961 | } 962 | } else XKillClient(dis, w); 963 | XFree(protocols); 964 | } 965 | 966 | void quit() { 967 | unsigned int i; 968 | client *c; 969 | 970 | logger("\033[0;34mYou Quit : Bye!"); 971 | for(i=0;inext) 975 | kill_client_now(c->win); 976 | } 977 | XClearWindow(dis, root); 978 | XUngrabKey(dis, AnyKey, AnyModifier, root); 979 | XSync(dis, False); 980 | XSetInputFocus(dis, root, RevertToPointerRoot, CurrentTime); 981 | bool_quit = 1; 982 | } 983 | 984 | unsigned long getcolor(const char* color) { 985 | XColor c; 986 | Colormap map = DefaultColormap(dis,screen); 987 | 988 | if(!XAllocNamedColor(dis,map,color,&c,&c)) 989 | logger("\033[0;31mError parsing color!"); 990 | 991 | return c.pixel; 992 | } 993 | 994 | void logger(const char* e) { 995 | fprintf(stderr,"\n\033[0;34m:: dminiwm : %s \033[0;m\n", e); 996 | } 997 | 998 | void setup() { 999 | unsigned int i; 1000 | 1001 | // Install a signal 1002 | sigchld(0); 1003 | 1004 | // Screen and root window 1005 | screen = DefaultScreen(dis); 1006 | root = RootWindow(dis,screen); 1007 | 1008 | bdw = BORDER_WIDTH; 1009 | panel_size = PANEL_HEIGHT; 1010 | // Screen width and height 1011 | sw = XDisplayWidth(dis,screen) - bdw; 1012 | sh = XDisplayHeight(dis,screen) - (panel_size+bdw); 1013 | 1014 | char *loc; 1015 | loc = setlocale(LC_ALL, ""); 1016 | if (!loc || !strcmp(loc, "C") || !strcmp(loc, "POSIX") || !XSupportsLocale()) 1017 | logger("LOCALE FAILED"); 1018 | 1019 | // For having the panel shown at startup or not 1020 | showbar = SHOW_BAR; 1021 | if(showbar != 0) toggle_panel(); 1022 | 1023 | // Colors 1024 | win_focus = getcolor(FOCUS); 1025 | win_unfocus = getcolor(UNFOCUS); 1026 | 1027 | // Shortcuts 1028 | grabkeys(); 1029 | 1030 | // Master size 1031 | 1032 | // Set up all desktop 1033 | for(i=0;i 0 && MODES[i] < 6) desktops[i].mode = MODES[i]-1; 1043 | else desktops[i].mode = DEFAULT_MODE-1; 1044 | desktops[i].master_size = (desktops[i].mode == 2) ? sh*MASTER_SIZE : sw*MASTER_SIZE; 1045 | } 1046 | 1047 | // Select first desktop by default 1048 | select_desktop(0); 1049 | wm_delete_window = XInternAtom(dis, "WM_DELETE_WINDOW", False); 1050 | protos = XInternAtom(dis, "WM_PROTOCOLS", False); 1051 | // To catch maprequest and destroynotify (if other wm running) 1052 | XSelectInput(dis,root,SubstructureNotifyMask|SubstructureRedirectMask); 1053 | // For exiting 1054 | bool_quit = 0; 1055 | logger("\033[0;32mWe're up and running!"); 1056 | update_info(); 1057 | } 1058 | 1059 | void sigchld(int unused) { 1060 | if(signal(SIGCHLD, sigchld) == SIG_ERR) { 1061 | logger("\033[0;31mCan't install SIGCHLD handler"); 1062 | exit(1); 1063 | } 1064 | while(0 < waitpid(-1, NULL, WNOHANG)); 1065 | } 1066 | 1067 | void spawn(const Arg arg) { 1068 | if(fork() == 0) { 1069 | if(fork() == 0) { 1070 | if(dis) close(ConnectionNumber(dis)); 1071 | setsid(); 1072 | execvp((char*)arg.com[0],(char**)arg.com); 1073 | } 1074 | exit(0); 1075 | } 1076 | } 1077 | 1078 | /* There's no way to check accesses to destroyed windows, thus those cases are ignored (especially on UnmapNotify's). Other types of errors call Xlibs default error handler, which may call exit. */ 1079 | int xerror(Display *dis, XErrorEvent *ee) { 1080 | if(ee->error_code == BadWindow || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 1081 | || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 1082 | || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 1083 | || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 1084 | || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 1085 | || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 1086 | || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 1087 | return 0; 1088 | if(ee->error_code == BadAccess) { 1089 | logger("\033[0;31mIs Another Window Manager Running? Exiting!"); 1090 | exit(1); 1091 | } else logger("\033[0;31mBad Window Error!"); 1092 | return xerrorxlib(dis, ee); /* may call exit */ 1093 | } 1094 | 1095 | void start() { 1096 | XEvent ev; 1097 | 1098 | while(!bool_quit && !XNextEvent(dis,&ev)) { 1099 | if(events[ev.type]) 1100 | events[ev.type](&ev); 1101 | } 1102 | } 1103 | 1104 | 1105 | int main() { 1106 | // Open display 1107 | if(!(dis = XOpenDisplay(NULL))) { 1108 | logger("\033[0;31mCannot open display!"); 1109 | exit(1); 1110 | } 1111 | XSetErrorHandler(xerror); 1112 | 1113 | // Setup env 1114 | setup(); 1115 | 1116 | // Start wm 1117 | start(); 1118 | 1119 | // Close display 1120 | XCloseDisplay(dis); 1121 | 1122 | exit(0); 1123 | } 1124 | --------------------------------------------------------------------------------