/* * /\___/\ * ( o o ) Made by cat... * ( =^= ) * ( ) ... for cat! * ( ) * ( ))))))________________ Cute And Tiny Window Manager * ______________________________________________________________________________ * * Copyright (c) 2010, Rinaldini Julien, julien.rinaldini@heig-vd.ch * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #define TABLENGTH(X) (sizeof(X)/sizeof(*X)) typedef union { const char** com; const int i; } Arg; // Structs struct key { unsigned int mod; KeySym keysym; void (*function)(const Arg arg); const Arg arg; }; typedef struct client client; struct client{ // Prev and next client client *next; client *prev; // The window Window win; }; typedef struct desktop desktop; struct desktop{ int master_size; int mode; client *head; client *current; }; // Functions static void add_window(Window w); static void change_desktop(const Arg arg); static void client_to_desktop(const Arg arg); static void configurenotify(XEvent *e); static void configurerequest(XEvent *e); static void decrease(); static void destroynotify(XEvent *e); static void die(const char* e); static unsigned long getcolor(const char* color); static void grabkeys(); static void increase(); static void keypress(XEvent *e); static void kill_client(); static void maprequest(XEvent *e); static void move_down(); static void move_up(); static void next_win(); static void prev_win(); static void quit(); static void remove_window(Window w); static void save_desktop(int i); static void select_desktop(int i); static void send_kill_signal(Window w); static void setup(); static void sigchld(int unused); static void spawn(const Arg arg); static void start(); //static void swap(); static void swap_master(); static void switch_mode(); static void tile(); static void update_current(); // Include configuration file (need struct key) #include "config.h" // Variable static Display *dis; static int bool_quit; static int current_desktop; static int master_size; static int mode; static int sh; static int sw; static int screen; static unsigned int win_focus; static unsigned int win_unfocus; static Window root; static client *head; static client *current; // Events array static void (*events[LASTEvent])(XEvent *e) = { [KeyPress] = keypress, [MapRequest] = maprequest, [DestroyNotify] = destroynotify, [ConfigureNotify] = configurenotify, [ConfigureRequest] = configurerequest }; // Desktop array static desktop desktops[10]; void add_window(Window w) { client *c,*t; if(!(c = (client *)calloc(1,sizeof(client)))) die("Error calloc!"); if(head == NULL) { c->next = NULL; c->prev = NULL; c->win = w; head = c; } else { for(t=head;t->next;t=t->next); c->next = NULL; c->prev = t; c->win = w; t->next = c; } current = c; } void change_desktop(const Arg arg) { client *c; if(arg.i == current_desktop) return; // Unmap all window if(head != NULL) for(c=head;c;c=c->next) XUnmapWindow(dis,c->win); // Save current "properties" save_desktop(current_desktop); // Take "properties" from the new desktop select_desktop(arg.i); // Map all windows if(head != NULL) for(c=head;c;c=c->next) XMapWindow(dis,c->win); tile(); update_current(); } void client_to_desktop(const Arg arg) { client *tmp = current; int tmp2 = current_desktop; if(arg.i == current_desktop || current == NULL) return; // Add client to desktop select_desktop(arg.i); add_window(tmp->win); save_desktop(arg.i); // Remove client from current desktop select_desktop(tmp2); remove_window(current->win); tile(); update_current(); } void configurenotify(XEvent *e) { // Do nothing for the moment } void configurerequest(XEvent *e) { // Paste from DWM, thx again \o/ XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.border_width = ev->border_width; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(dis, ev->window, ev->value_mask, &wc); } void decrease() { if(master_size > 50) { master_size -= 10; tile(); } } void destroynotify(XEvent *e) { int i=0; client *c; XDestroyWindowEvent *ev = &e->xdestroywindow; // Uber (and ugly) hack ;) for(c=head;c;c=c->next) if(ev->window == c->win) i++; // End of the hack if(i == 0) return; remove_window(ev->window); tile(); update_current(); } void die(const char* e) { fprintf(stdout,"catwm: %s\n",e); exit(1); } unsigned long getcolor(const char* color) { XColor c; Colormap map = DefaultColormap(dis,screen); if(!XAllocNamedColor(dis,map,color,&c,&c)) die("Error parsing color!"); return c.pixel; } void grabkeys() { int i; KeyCode code; // For each shortcuts for(i=0;ixkey; KeySym keysym = XkbKeycodeToKeysym(dis,ke.keycode,0,0); for(i=0;iwin; ke.xclient.message_type = XInternAtom(dis, "WM_PROTOCOLS", True); ke.xclient.format = 32; ke.xclient.data.l[0] = XInternAtom(dis, "WM_DELETE_WINDOW", True); ke.xclient.data.l[1] = CurrentTime; XSendEvent(dis, current->win, False, NoEventMask, &ke); send_kill_signal(current->win); } } void maprequest(XEvent *e) { XMapRequestEvent *ev = &e->xmaprequest; // For fullscreen mplayer (and maybe some other program) client *c; for(c=head;c;c=c->next) if(ev->window == c->win) { XMapWindow(dis,ev->window); return; } add_window(ev->window); XMapWindow(dis,ev->window); tile(); update_current(); } void move_down() { Window tmp; if(current == NULL || current->next == NULL || current->win == head->win || current->prev == NULL) { return; } tmp = current->win; current->win = current->next->win; current->next->win = tmp; //keep the moved window activated next_win(); tile(); update_current(); } void move_up() { Window tmp; if(current == NULL || current->prev == head || current->win == head->win) { return; } tmp = current->win; current->win = current->prev->win; current->prev->win = tmp; prev_win(); tile(); update_current(); } void next_win() { client *c; if(current != NULL && head != NULL) { if(current->next == NULL) c = head; else c = current->next; current = c; update_current(); } } void prev_win() { client *c; if(current != NULL && head != NULL) { if(current->prev == NULL) for(c=head;c->next;c=c->next); else c = current->prev; current = c; update_current(); } } void quit() { Window root_return, parent; Window *children; int i; unsigned int nchildren; XEvent ev; /* * if a client refuses to terminate itself, * we kill every window remaining the brutal way. * Since we're stuck in the while(nchildren > 0) { ... } loop * we can't exit through the main method. * This all happens if MOD+q is pushed a second time. */ if(bool_quit == 1) { XUngrabKey(dis, AnyKey, AnyModifier, root); XDestroySubwindows(dis, root); fprintf(stdout, "catwm: Thanks for using!\n"); XCloseDisplay(dis); die("forced shutdown"); } bool_quit = 1; XQueryTree(dis, root, &root_return, &parent, &children, &nchildren); for(i = 0; i < nchildren; i++) { send_kill_signal(children[i]); } //keep alive until all windows are killed while(nchildren > 0) { XQueryTree(dis, root, &root_return, &parent, &children, &nchildren); XNextEvent(dis,&ev); if(events[ev.type]) events[ev.type](&ev); } XUngrabKey(dis,AnyKey,AnyModifier,root); fprintf(stdout,"catwm: Thanks for using!\n"); } void remove_window(Window w) { client *c; // CHANGE THIS UGLY CODE for(c=head;c;c=c->next) { if(c->win == w) { if(c->prev == NULL && c->next == NULL) { free(head); head = NULL; current = NULL; return; } if(c->prev == NULL) { head = c->next; c->next->prev = NULL; current = c->next; } else if(c->next == NULL) { c->prev->next = NULL; current = c->prev; } else { c->prev->next = c->next; c->next->prev = c->prev; current = c->prev; } free(c); return; } } } void save_desktop(int i) { desktops[i].master_size = master_size; desktops[i].mode = mode; desktops[i].head = head; desktops[i].current = current; } void select_desktop(int i) { head = desktops[i].head; current = desktops[i].current; master_size = desktops[i].master_size; mode = desktops[i].mode; current_desktop = i; } void send_kill_signal(Window w) { XEvent ke; ke.type = ClientMessage; ke.xclient.window = w; ke.xclient.message_type = XInternAtom(dis, "WM_PROTOCOLS", True); ke.xclient.format = 32; ke.xclient.data.l[0] = XInternAtom(dis, "WM_DELETE_WINDOW", True); ke.xclient.data.l[1] = CurrentTime; XSendEvent(dis, w, False, NoEventMask, &ke); } void setup() { // Install a signal sigchld(0); // Screen and root window screen = DefaultScreen(dis); root = RootWindow(dis,screen); // Screen width and height sw = XDisplayWidth(dis,screen); sh = XDisplayHeight(dis,screen); // Colors win_focus = getcolor(FOCUS); win_unfocus = getcolor(UNFOCUS); // Shortcuts grabkeys(); // Vertical stack mode = 0; // For exiting bool_quit = 0; // List of client head = NULL; current = NULL; // Master size master_size = sw*MASTER_SIZE; // Set up all desktop int i; for(i=0;iwin; head->win = current->win; current->win = tmp; current = head; tile(); update_current(); } } void switch_mode() { mode = (mode == 0) ? 1:0; tile(); update_current(); } void tile() { client *c; int n = 0; int y = 0; // If only one window if(head != NULL && head->next == NULL) { XMoveResizeWindow(dis,head->win,0,0,sw-2,sh-2); } else if(head != NULL) { switch(mode) { case 0: // Master window XMoveResizeWindow(dis,head->win,0,0,master_size-2,sh-2); // Stack for(c=head->next;c;c=c->next) ++n; for(c=head->next;c;c=c->next) { XMoveResizeWindow(dis,c->win,master_size,y,sw-master_size-2,(sh/n)-2); y += sh/n; } break; case 1: for(c=head;c;c=c->next) { XMoveResizeWindow(dis,c->win,0,0,sw,sh); } break; default: break; } } } void update_current() { client *c; for(c=head;c;c=c->next) if(current == c) { // "Enable" current window XSetWindowBorderWidth(dis,c->win,1); XSetWindowBorder(dis,c->win,win_focus); XSetInputFocus(dis,c->win,RevertToParent,CurrentTime); XRaiseWindow(dis,c->win); } else XSetWindowBorder(dis,c->win,win_unfocus); } int main(int argc, char **argv) { // Open display if(!(dis = XOpenDisplay(NULL))) die("Cannot open display!"); // Setup env setup(); // Start wm start(); // Close display XCloseDisplay(dis); return 0; }