├── config ├── writefifo ├── commands └── .xbindkeysrc ├── src ├── responses.h ├── window.h ├── events.h ├── atoms.h ├── lookup.h ├── util.h ├── commands.h ├── window.c ├── structs.h ├── tree.h ├── foo-wm.h ├── config.def.h ├── client.c ├── atoms.c ├── lookup.c ├── util.c ├── responses.c ├── events.c ├── foo-wm.c ├── commands.c └── tree.c ├── scripts ├── client.rb ├── marks.rb └── uncontainerize.rb ├── Makefile ├── TODO.md ├── LICENSE ├── README.md └── FOO-DSL.md /config/writefifo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo $@ > /home/mil/fifos/wm-fifo 3 | -------------------------------------------------------------------------------- /src/responses.h: -------------------------------------------------------------------------------- 1 | char * jsonMarks(void); 2 | char * jsonTree(Node * node, int level); 3 | -------------------------------------------------------------------------------- /src/window.h: -------------------------------------------------------------------------------- 1 | void centerPointer(Window *window); 2 | void setCursor(Window *window, int cursor); 3 | -------------------------------------------------------------------------------- /src/events.h: -------------------------------------------------------------------------------- 1 | void handleXEvent(XEvent *event); 2 | void eMapRequest(XEvent *event); 3 | void eButtonPress(XEvent *event); 4 | -------------------------------------------------------------------------------- /src/atoms.h: -------------------------------------------------------------------------------- 1 | enum { WmProtocols, WmDeleteWindow, WmCount }; 2 | Atom icccmAtoms[WmCount]; 3 | 4 | void sendDeleteWindow(Window * window); 5 | void setupAtoms(void); 6 | -------------------------------------------------------------------------------- /src/lookup.h: -------------------------------------------------------------------------------- 1 | Node * getNodeByWindow(Window * window); 2 | Node * getNodeById(int id); 3 | void removeLookupEntry(Window * window); 4 | void addLookupEntry( Node * node, Window * window); 5 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | unsigned long getColor(const char *colstr); 2 | int xError(XErrorEvent *e); 3 | Node * allocateNode(); 4 | void gridDimensions(int children, int * rows, int * cols); 5 | Bool isPrime(int number); 6 | Node * focusOrChildOf(Node * node); 7 | int directionStringToInt(char * directionString); 8 | void recalculateRootDimensions (void); 9 | int bytesUntilNull(char * string); 10 | -------------------------------------------------------------------------------- /config/commands: -------------------------------------------------------------------------------- 1 | dump 2 | jump 3 | mark 4 | kill 5 | containerize 6 | zoom 7 | focus 8 | focus brother 9 | focus pc 10 | shift 11 | shift brother 12 | shift pc 13 | layout 14 | layout grid 15 | layout horizontal 16 | layout vertical 17 | layout max 18 | set 19 | set client_border_width 20 | set container_padding 21 | set screen_padding_top 22 | set screen_padding_left 23 | set screen_padding_bottom 24 | set screen_padding_right 25 | -------------------------------------------------------------------------------- /scripts/client.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'socket' 3 | $socketPath = "/tmp/foo-wm.socket" 4 | 5 | def sendCommand(command) 6 | sock = UNIXSocket.open($socketPath) 7 | sock.send command, 0 8 | loop do 9 | if (sock.recv(1, Socket::MSG_PEEK) == "") then 10 | break 11 | end 12 | print sock.recv(1) 13 | end 14 | end 15 | 16 | if (ARGV.size == 0) then 17 | puts "No command provided" 18 | else 19 | sendCommand(ARGV.join(' ')) 20 | end 21 | -------------------------------------------------------------------------------- /src/commands.h: -------------------------------------------------------------------------------- 1 | char * handleCommand(char * request); 2 | char * nextToken(char ** tokenString); 3 | 4 | void absorb(char * argA, char * argB); 5 | void containerize(void); 6 | void focus(char * argA, char * argB); 7 | char * get(char * property); 8 | void kill(void); 9 | void jump(char * markName); 10 | void layout(char * l); 11 | void mark(char * markName); 12 | void set(char * property, char * value); 13 | void shift(char * argA, int delta); 14 | Bool swap(char * argA , char * argB); 15 | void zoom(int level); 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | PRECFLAGS=-Wall -g 3 | POSTCFLAGS=-lX11 -lm 4 | VPATH=./src 5 | 6 | WM_OBJS= atoms.o events.o tree.o commands.o responses.o util.o window.o lookup.o foo-wm.o 7 | CLI_OBJS= client.o 8 | 9 | all: foo-wm foo-wm-c 10 | 11 | src/config.h: 12 | cp src/config.def.h src/config.h 13 | 14 | foo-wm: src/config.h $(WM_OBJS) 15 | $(CC) $(PRECFLAGS) $(WM_OBJS) $(POSTCFLAGS) -o foo-wm 16 | 17 | foo-wm-c: $(CLI_OBJS) 18 | $(CC) $(PRECFLAGS) $(CLI_OBJS) $(POSTCFLAGS) -o foo-wm-c 19 | 20 | clean: 21 | rm -rf foo-wm foo-wm-c *.o 22 | -------------------------------------------------------------------------------- /scripts/marks.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'socket' 3 | require 'json' 4 | require 'pp' 5 | $socketPath = "/tmp/foo-wm.socket" 6 | 7 | def sendCommand(command) 8 | response = "" 9 | sock = UNIXSocket.open($socketPath) 10 | sock.send command, 0 11 | loop do 12 | if (sock.recv(1, Socket::MSG_PEEK) == "") then 13 | break 14 | end 15 | response = "#{response}#{sock.recv(1)}" 16 | end 17 | 18 | return response 19 | end 20 | 21 | tree = sendCommand('get marks')[0..-2] 22 | (JSON tree)["marks"].each do |mark| 23 | puts mark 24 | end 25 | 26 | -------------------------------------------------------------------------------- /src/window.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "foo-wm.h" 4 | #include "window.h" 5 | 6 | void centerPointer(Window *window) { 7 | //Get Window Attributes 8 | XWindowAttributes windowAttributes; 9 | XGetWindowAttributes(display, *window, &windowAttributes); 10 | 11 | int centerX = windowAttributes.width / 2, 12 | centerY = windowAttributes.height / 2; 13 | 14 | //Warp to Center 15 | XWarpPointer(display, None, *window, 0, 0, 0, 0, centerX,centerY); 16 | } 17 | 18 | void setCursor(Window *window, int cursor) { 19 | cursor = XCreateFontCursor(display, cursor); 20 | XDefineCursor(display, *window, cursor); 21 | } 22 | -------------------------------------------------------------------------------- /src/structs.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | enum { VERTICAL, HORIZONTAL, GRID, MAX, TABBED, FLOAT, FREEFLOAT }; 4 | enum { PREVIOUS, NEXT }; 5 | 6 | 7 | typedef struct Node Node; 8 | struct Node { 9 | int layout; 10 | int x, y, width, height; 11 | 12 | Node *parent; 13 | Node *next; 14 | Node *previous; 15 | Node *focus; 16 | 17 | /* If a Container */ 18 | Node *child; 19 | //Node *focus; 20 | 21 | /* If a Client */ 22 | Window window; 23 | }; 24 | 25 | /* Struct used for looking up a window's associated client */ 26 | typedef struct Lookup Lookup; 27 | struct Lookup { 28 | int window; 29 | Node *node; 30 | Lookup *previous; 31 | }; 32 | 33 | typedef struct Mark Mark; 34 | struct Mark { 35 | char *name; 36 | Node * node; 37 | Mark * previous; 38 | }; 39 | -------------------------------------------------------------------------------- /scripts/uncontainerize.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'socket' 3 | require 'json' 4 | require 'pp' 5 | $socketPath = "/tmp/foo-wm.socket" 6 | 7 | def sendCommand(command) 8 | response = "" 9 | sock = UNIXSocket.open($socketPath) 10 | sock.send command, 0 11 | loop do 12 | if (sock.recv(1, Socket::MSG_PEEK) == "") then 13 | break 14 | end 15 | response = "#{response}#{sock.recv(1)}" 16 | end 17 | 18 | return response 19 | end 20 | 21 | jsonTree = sendCommand('get focus')[0..-2] 22 | 23 | tree = JSON.parse(jsonTree); 24 | if (tree["type"] != "container") then 25 | puts "uncontainerize may only be used on a container" 26 | exit 27 | end 28 | 29 | tree["children"].each do |child| 30 | nodeId = child["id"] 31 | sendCommand("focus id #{child['id']}") 32 | sendCommand("shift pc -1") 33 | end 34 | -------------------------------------------------------------------------------- /src/tree.h: -------------------------------------------------------------------------------- 1 | Bool isClient(Node * node); 2 | Bool isOnlyChild(Node * node); 3 | Bool nodeIsParentOf(Node * nodeA, Node * nodeB); 4 | Bool unfocusNode(Node * n, Bool focusPath); 5 | 6 | char * crawlNode(Node * node, int level); 7 | long getBorderColor(Node * node, Bool focusPath); 8 | 9 | Node * getBrother(Node * node, int delta); 10 | Node * getBrotherClient(Node * node, int direction); 11 | Node * getClosestClient(Node * node); 12 | 13 | void brotherNode(Node *node, Node * brother, int position); 14 | void destroyNode(Node * n); 15 | void dumpTree(); 16 | void focusNode(Node * n, XEvent * event, Bool setFocused, Bool focusPath); 17 | void parentNode(Node *node, Node *parent); 18 | void placeNode(Node * node, int x, int y, int width, int height); 19 | void rePlaceNode(Node * node); 20 | void swapNodes(Node * a, Node * b); 21 | void unmapNode(Node * node); 22 | void unparentNode(Node *node); 23 | -------------------------------------------------------------------------------- /src/foo-wm.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "structs.h" 4 | 5 | //X Server 6 | Display *display; 7 | int screen, activeScreen; 8 | int rootX, rootY, rootWidth, rootHeight; 9 | Window root; 10 | 11 | /* focusedNode :: Currently focused node (innard most of fcs ptrs rcvs input) 12 | * viewNode :: Currently viewed node (screenspace) 13 | * rootNode :: Node at the top of the tree*/ 14 | Node *focusedNode, *viewNode, *rootNode; 15 | 16 | /* X Window -> Node Lookup Table */ 17 | Lookup * lookupTail; 18 | Mark * markTail; 19 | 20 | //Customizations 21 | int clientPadding, containerPadding; 22 | int screenPaddingLeft, screenPaddingRight, screenPaddingTop, screenPaddingBottom; 23 | int defaultLayout, border; 24 | long activeFocusedColor, activeUnfocusedColor, 25 | inactiveFocusedColor, inactiveUnfocusedColor; 26 | 27 | void setup(void); 28 | void handleEvents(void); 29 | -------------------------------------------------------------------------------- /src/config.def.h: -------------------------------------------------------------------------------- 1 | /* Path to store socket */ 2 | #define SOCKET_PATH "/tmp/" 3 | /* If "NONE" is specified as the SOCKET_NAME, 4 | * one will automatically be generated based the sockets PID 5 | * In the form of: foo-wm-pid.socket */ 6 | #define SOCKET_NAME "foo-wm.socket" 7 | 8 | // Screen Padding 9 | // I leave 18 px height space for LemonBoy's bar 10 | // (could use dzen, conky, etc here) 11 | #define SCREEN_PADDING_TOP (18) 12 | #define SCREEN_PADDING_LEFT (0) 13 | #define SCREEN_PADDING_BOTTOM (0) 14 | #define SCREEN_PADDING_RIGHT (0) 15 | 16 | // Containers 17 | #define CONTAINER_DEFAULT_LAYOUT (GRID) 18 | #define CONTAINER_PADDING (2) 19 | 20 | #define CLIENT_ACTIVE_FOCUSED_COLOR "rgb:ab/db/f5" 21 | #define CLIENT_ACTIVE_UNFOCUSED_COLOR "rgb:ff/00/00" 22 | #define CLIENT_INACTIVE_FOCUSED_COLOR "rgb:a7/ff/87" 23 | #define CLIENT_INACTIVE_UNFOCUSED_COLOR "rgb:54/54/54" 24 | 25 | #define CLIENT_BORDER_WIDTH (1) 26 | #define CLIENT_PADDING (0) 27 | -------------------------------------------------------------------------------- /src/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) { 9 | int socketFd; 10 | struct sockaddr_un socketAddress; 11 | int bufferSize = 256; 12 | char buffer[bufferSize]; 13 | 14 | /* Initialize the sockaddr_un struct */ 15 | socketAddress.sun_family = AF_UNIX; 16 | strcpy(socketAddress.sun_path, argv[1]); 17 | 18 | /* Setup the socket, Connect, and Send the send CL Arg */ 19 | socketFd = socket(AF_UNIX, SOCK_STREAM, 0); 20 | connect(socketFd, (struct sockaddr *)&socketAddress, sizeof(socketAddress)); 21 | send(socketFd, argv[2], strlen(argv[2]), 0); 22 | 23 | /* Check if we recieved a response */ 24 | while (recv(socketFd, buffer, sizeof(buffer), 0) > 0) { 25 | buffer[sizeof(buffer)] = '\0'; 26 | printf("%s", buffer); 27 | } 28 | 29 | /* Close and return */ 30 | printf("\n"); 31 | close(socketFd); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/atoms.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "atoms.h" 6 | #include "foo-wm.h" 7 | 8 | void sendDeleteWindow(Window * window) { 9 | int protocolsReturn = -1; 10 | Atom *protocols = NULL; 11 | XGetWMProtocols(display, *window, &protocols, &protocolsReturn); 12 | 13 | XEvent dw; 14 | dw.type = ClientMessage; 15 | dw.xclient.window = *window; 16 | dw.xclient.format = 32; 17 | dw.xclient.message_type = icccmAtoms[WmProtocols]; 18 | dw.xclient.data.l[0] = icccmAtoms[WmDeleteWindow]; 19 | dw.xclient.data.l[1] = CurrentTime; 20 | XSendEvent(display, *window, False, NoEventMask, &dw); 21 | 22 | } 23 | 24 | 25 | void setupAtoms(void) { 26 | // Set Up ICCCM Atoms for: 27 | // WM_PROTOCOLS & WM_DELETE_WINDOW 28 | icccmAtoms[WmProtocols] = XInternAtom( 29 | display, "WM_PROTOCOLS", False); 30 | icccmAtoms[WmDeleteWindow] = XInternAtom( 31 | display, "WM_DELETE_WINDOW", False); 32 | 33 | XSync(display, False); 34 | } 35 | -------------------------------------------------------------------------------- /src/lookup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #include "foo-wm.h" 7 | #include "util.h" 8 | 9 | //Returns the client associated with given windowv 10 | Node * getNodeByWindow(Window * window) { 11 | Lookup *entry; 12 | for (entry = lookupTail; entry != NULL; entry = entry -> previous) 13 | if (((int) *window) == entry -> window) 14 | return entry -> node; 15 | 16 | return NULL; 17 | } 18 | 19 | Node * getNodeById(int id) { 20 | Lookup *entry; 21 | for (entry = lookupTail; entry != NULL; entry = entry -> previous) 22 | if (id == (int) entry -> node -> window) 23 | return entry -> node; 24 | 25 | return NULL; 26 | } 27 | 28 | void removeLookupEntry(Window * window) { 29 | Lookup *cut = NULL; 30 | Lookup *entry = NULL; 31 | 32 | //Removing last entry in list 33 | if (lookupTail -> window == (int) *window) { 34 | entry = lookupTail -> previous; 35 | free(lookupTail); 36 | lookupTail = entry; 37 | return; 38 | } 39 | 40 | //Rmove any other entry in list 41 | for (entry = lookupTail; entry -> previous != NULL; entry = entry -> previous) { 42 | if ((int)*window == entry -> previous -> window) { 43 | cut = entry -> previous; 44 | entry -> previous = entry -> previous -> previous; 45 | free(cut); 46 | return; 47 | } 48 | } 49 | } 50 | 51 | void addLookupEntry( Node * node, Window * window) { 52 | //Add Client and window to lookup list 53 | Lookup *entry = malloc(sizeof(Lookup)); 54 | entry -> node = node; 55 | entry -> window = (int) *window; 56 | 57 | //Set lookup (last item) as aour previous, and now were lookup (last) 58 | entry -> previous = lookupTail; 59 | lookupTail = entry; 60 | } 61 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | This is an ongoing list of bugs and planned features. If you are looking for something to contribute, this would be a good starting point. The codebase of foo-wm is small and hackable. Contributions are welcomed. 2 | 3 | Bugs 4 | ==== 5 | - 'set screen_padding_(top|left|right|bottom)' does not take immediate affect 6 | - 1 Child containers within containers 7 | - 'containerize' 'containerize' -- case of the missing container 8 | - brotherNode() in tree.c, node->parent = brother->parent ? 9 | * This could potentially break the old node's brothers and node parent child ptr 10 | - XMapWindow and XRaiseWindow always called for Windows in placeNode regardless if already mapped/raised 11 | - Destroying last client doesn't focus container / set focusedNode 12 | * Tree: 13 | - Container (1) 14 | * Container (2) 15 | * Client (3) 16 | * Client (4) 17 | * Client (5) [Focus] 18 | * 'kill' 'kill' 'kill' 'focus pc -1' ==> causes crash 19 | * void focus asserts focusedNode? 20 | - mark'ing and jump'ing to a client doesn't restore the client but the container 21 | - First client launched if no root (on start) should be just a client and not part of a container 22 | * When 2nd client launched, if there is a client, a container should be created 23 | 24 | Fixed Bugs 25 | ========== 26 | - 'focus brother delta' updates view node, but does not set global viewNode 27 | 28 | 29 | Planned Features 30 | ================ 31 | - Implement Floating Layout for Containers 32 | * Mostly will be an update to placeNode in tree.c 33 | * Will need to modify events.c as well 34 | - WM Hints/Atoms 35 | - reparent command (shifts a node into another container) 36 | - transpose command (given two node ids - switch) 37 | - Socket responses for commands in command.c 38 | - Title and Tab bar / Window Decorations 39 | - Multi-Monitor 40 | - Percent Fill for Nodes whose parent is Vertical/Horizontal mode (Resize) 41 | - If 2 (Container) Nodes in a Parent, and 1 Destroyed --> Last Container becomes Client 42 | - kill command should work for container nodes not just client nodes 43 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | #include "foo-wm.h" 9 | #include "util.h" 10 | 11 | //Thank you DWM ;) 12 | unsigned long getColor(const char *colstr) { 13 | Colormap cmap = DefaultColormap(display, activeScreen); 14 | XColor color; 15 | 16 | if(!XAllocNamedColor(display, cmap, colstr, &color, &color)) { return 0; } 17 | return color.pixel; 18 | } 19 | 20 | 21 | int xError(XErrorEvent *e) { 22 | fprintf(stderr, "XErrorEvent of Request Code: %d and Error Code of %d\n", e -> request_code, e -> error_code); 23 | return 0; 24 | } 25 | 26 | Node * allocateNode() { 27 | 28 | Node *n = malloc(sizeof(Node)); 29 | n -> previous = NULL; n -> next = NULL; 30 | n -> parent = NULL; n -> child = NULL; 31 | n -> focus = NULL; 32 | n -> window = (Window) NULL; 33 | n -> layout = defaultLayout; 34 | return n; 35 | } 36 | 37 | 38 | void recalculateRootDimensions (void) { 39 | if (!rootNode) return; 40 | rootNode -> x = rootX = screenPaddingLeft; 41 | rootNode -> y = rootY = screenPaddingTop; 42 | rootNode -> width = rootWidth = DisplayWidth(display, activeScreen) - screenPaddingLeft - screenPaddingRight; 43 | rootNode -> height = rootHeight = DisplayHeight(display, activeScreen) - screenPaddingTop - screenPaddingBottom; 44 | } 45 | 46 | void gridDimensions(int children, int * rows, int * cols) { 47 | if (children == 2) { *rows = 1; *cols = 2; return; } /* Edgecase */ 48 | 49 | int square = (int) sqrt(children); 50 | int r = square; 51 | 52 | while (((children % r) != 0)) { r++; } 53 | int c = children / r; 54 | 55 | if ((r == 1 && c != 1) || (c == 1 && r != 1)) { 56 | gridDimensions(children + 1, rows, cols); 57 | } else { 58 | *rows = r; 59 | *cols = c; 60 | } 61 | } 62 | 63 | Bool isPrime(int number) { 64 | Bool prime = True; 65 | int i; 66 | for(i = 2; i <= number / 2; i++) 67 | if(number % i == 0) { prime = False; break; } 68 | return prime; 69 | } 70 | 71 | Node * focusOrChildOf(Node * node) { 72 | if (!node) return NULL; 73 | else if (node -> focus) return node -> focus; 74 | else return node -> child; 75 | } 76 | int bytesUntilNull(char * string) { 77 | int counter = 0; 78 | while (string[counter] != '\0') 79 | counter++; 80 | 81 | /* Include the NULL Byte */ 82 | return ++counter; 83 | } 84 | -------------------------------------------------------------------------------- /src/responses.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "foo-wm.h" 5 | #include "util.h" 6 | #include "tree.h" 7 | #include "responses.h" 8 | 9 | 10 | char * jsonMarks(void) { 11 | char * marksResponse = malloc(1024); 12 | sprintf(marksResponse, "{\"marks\":["); 13 | Mark *n = NULL; 14 | for(n = markTail; n; n = n -> previous) { 15 | sprintf(marksResponse, 16 | "%s%s\"%s\"", 17 | marksResponse, 18 | n != markTail ? "," : "", 19 | n -> name 20 | ); 21 | } 22 | 23 | sprintf(marksResponse, "%s]}", marksResponse); 24 | realloc(marksResponse, bytesUntilNull(marksResponse)); 25 | return marksResponse; 26 | } 27 | 28 | 29 | char * jsonTree(Node * node, int level) { 30 | if (!node) return "Foo"; 31 | char 32 | *buffer = malloc(5120), 33 | *label = "", 34 | *nestString = ""; 35 | 36 | /* Recursivly Crawl Children */ 37 | if (!isClient(node)) { 38 | nestString = malloc(5120); 39 | Node *n = NULL; int c = 0; 40 | 41 | int last = -1; 42 | for (n = node -> child; n; n = n -> next, last++) { } 43 | n = NULL; 44 | 45 | for (n = node -> child; n; n = n -> next, c++) { 46 | char *crawlResult = malloc(5120); 47 | crawlResult = jsonTree(n, level + 1); 48 | sprintf(nestString, "%s%s%s", 49 | c == 0 ? "" : nestString, crawlResult, c != last ? "," : ""); 50 | free(crawlResult); 51 | } 52 | } 53 | 54 | 55 | /* Determine Label for sprintf */ 56 | if (!isClient(node)) { 57 | switch (node -> layout) { 58 | case VERTICAL : label = ",\"layout\":\"vertical\""; break; 59 | case HORIZONTAL : label = ",\"layout\":\"horizontal\""; break; 60 | case GRID : label = ",\"layout\":\"grid\""; break; 61 | case MAX : label = ",\"layout\":\"max\""; break; 62 | case FLOAT : label = ",\"layout\":\"float\""; break; 63 | } 64 | } 65 | 66 | /* Print to the buffer */ 67 | sprintf(buffer, 68 | /* Type (Pointer) (?Layout) (?R/V/F) (?ChildFocus) */ 69 | "{ \"node\":\"%p\",\"id\":\"%p\",\"type\":\"%s\"%s%s%s%s,\"focus\":\"%p\",\"children\":[%s]}", 70 | node, 71 | node -> window, 72 | isClient(node) ? "client" : "container", 73 | label, 74 | node == rootNode ? ",\"root\":\"true\"" : "", 75 | node == focusedNode ? ",\"focus\":\"true\"" : "", 76 | node == viewNode ? ",\"view\":\"true\"" : "", 77 | node -> focus ? node -> focus : "", 78 | nestString 79 | ); 80 | 81 | /* If it was a container, we malloc'd room for nestString */ 82 | if (sizeof(nestString) > sizeof(char*)) 83 | free(nestString); 84 | 85 | realloc(buffer, bytesUntilNull(buffer)); 86 | return buffer; 87 | } 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Miles Sandlar 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | 9 | -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 10 | 11 | Parts of DWM are also included in this project and licensed as follows: 12 | 13 | MIT/X Consortium License 14 | 15 | © 2006-2011 Anselm R Garbe 16 | © 2007-2011 Peter Hartlich 17 | © 2010-2011 Connor Lane Smith 18 | © 2006-2009 Jukka Salmi 19 | © 2007-2009 Premysl Hruby 20 | © 2007-2009 Szabolcs Nagy 21 | © 2007-2009 Christof Musik 22 | © 2009 Mate Nagy 23 | © 2007-2008 Enno Gottox Boland 24 | © 2008 Martin Hurton 25 | © 2008 Neale Pickett 26 | © 2006-2007 Sander van Dijk 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a 29 | copy of this software and associated documentation files (the "Software"), 30 | to deal in the Software without restriction, including without limitation 31 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 32 | and/or sell copies of the Software, and to permit persons to whom the 33 | Software is furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in 36 | all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 41 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 43 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | foo-wm: Foo Window Manager 2 | ============================ 3 | Foo Window Manager is a minimalistic window manager that does two things and two things only: 4 | 5 | 1. Provides a tree data structure in which you can organize windows. 6 | 2. Provides a socket for IPC along with a basic DSL for manipulating and traversing the tree. 7 | 8 | As a results of *only* doing these two things, `foo-wm` most likely works a bit differently than other window managers you may be acquainted with. Mainly, strangest of all to the newcomer, `foo-wm` **does not provide**: workspaces or keybindings. The former can be emulated through foo-wm's tree and the latter can be provided through any X keybinding program (such as `xbindkeys`). 9 | 10 | > A foreword: `foo-wm` should be considered unusable alpha software. I use `foo-wm` on a daily basis, although that doesn't mean you should (unless you are interested in contributing). `foo-wm` doesn't have full support for many basic window managment functions (ICCCM/EWMH). Although, ICCCM/EWMH support is a top priority currently. 11 | 12 | ### The Makefile will generate: 13 | `foo-wm`: The window manager itself which can be thrown in `.xinitrc`. It is reccomeneded that you start `foo-wm` with a keybindings manager, such as `.xbindkeys`, as it will be very diffucult to interact with `foo-wm`'s socket otherwise. The location and name of the socket, is to be specified in `src/config.h`. If no `SOCKET_NAME`, is specified then a name will be automatically generated based on the PID of the socket in the form of `/tmp/foo-wm-pid.socket`. 14 | 15 | `foo-wm-c`: This is a simple and dumb command line client for interacting with `foo-wm`. `foo-wm-c` takes the syntax of `foo-wm-c '/tmp/your-foo-socket.socket' 'ipc command'`. Any language that provides a UNIX socket interface can be used to communicate with `foo-wm`, `foo-wm-c` is just a simple C client for doing so. Additionally a ruby client for interacting with `foo-wm`'s socket can be found at `scrips/client.rb` 16 | 17 | The Tree 18 | -------- 19 | All operations in `foo-wm` are related to manipulating and the traversal of the tree. All nodes within the tree are one of two types. 20 | 21 | 1. **Client** Nodes 22 | - Just a single X11 Window 23 | 2. **Container** Nodes 24 | - Holds one or more other **Client** or **Container** Nodes 25 | - Has a *Layout* property which may be changed with the *layout* IPC command 26 | 27 | At any given time using `foo-wm` there are three essential nodes within the tree at play: 28 | 29 | 1. **Root** Node 30 | - The top of the tree 31 | - Issuing a zoom command with a negative delta approximates this node 32 | 2. **Focus** Node 33 | - The node which is currently manipulated on by using IPC commands 34 | - Issuing a zoom command with a positive delta approximates this node 35 | 3. **View** Node 36 | - The node which the screen is currently viewing 37 | 38 | 39 | The FOO-DSL and the Zooming Nature 40 | --------------------------- 41 | Now that you understand that all foo-wm does is maintain a tree of windows, now you'll want to know how to mainuplate the windows in the tree. For this, you simply send commands to foo-wm in the specifications provided in `FOO-DSL.md`. 42 | 43 | For example, sending the zoom command through the provided command line client. 44 | `foo-wm-c '/tmp/foo-wm.socket' 'zoom 2'` 45 | 46 | 47 | Inspiration 48 | ----------- 49 | Inspirations include: 50 | DWM, monsterwm, catwm, dminiwm, i3, herbstluftwm, wmii, and tinywm 51 | 52 | Contributing 53 | ------------ 54 | Foo Window Manager is very much open to contribution. Please get in touch via email if you are interested or just take a stab at something in `TODO.md` and send a pull request. 55 | -------------------------------------------------------------------------------- /FOO-DSL.md: -------------------------------------------------------------------------------- 1 | Foo DSL 2 | ====== 3 | The Foo DSL is foo-wm's set of commands which are accepted from the socket. 4 | 5 | ## `get` *`nodeIdentifier`* 6 | Gets a json representation of the tree with the parent being the `nodeIdentifier`. For example to get the JSON from what's visible do a: `get tree`. Or get the currently focused window do a `get focus`. 7 | 8 | - *Node Identifiers*: 9 | * `tree` 10 | * `view` 11 | * `focus` 12 | 13 | ## `set` *`propertyName` `propertyValue`* 14 | Sets an global `propertyName` to `propertyValue`. For example to make clients border width 30 pixels simply issue a `set client_border_width 30`. The screen_padding_direction globals can be used to make room for a bar such as [dzen](), [conky](), [bar](), or similar minimal X bars. 15 | 16 | - *Properties Names and Types*: 17 | * `client_border_width` : (integer) 18 | * `container_padding` : (integer) 19 | * `client_padding` : (integer) 20 | * `screen_padding_top` : (integer) 21 | * `screen_padding_left` : (integer) 22 | * `screen_padding_right` : (integer) 23 | * `screen_padding_bottom` : (integer) 24 | 25 | ## `zoom` *`(-)delta`* 26 | Given that the **View Node** tracks what is currently on the screen, you can zoom in and out to see the full tree or only a part of the tree by simply by manipulating the **View Node**. `zoom -1000` would zoom out or travel up a thousand levels in ancestory within the tree. `zoom 3` would zoom in 3 levels. 27 | 28 | In foo-wm there is no fullscreen command, rather issuing zoom until the **View Node** hits the **Focus Node** is equivilant going "fullscreen". Functionality of a fullscreen kind of action can be easily implemented to jump back and forth in maximizing 1 window and then traveling back to a "zoomed out" view via scripting and the get command. 29 | 30 | 31 | ## `layout` *`type`* 32 | Manipulates the **Focus Node**. Sets the layout type of the focus node. This is useful for displaying certain windows within a container node. For example, `layout vertical` tiles nodes vertically. 33 | 34 | * Layout Options 35 | - Currently Implemented 36 | * `vertical`: Children are arranged side by side vertically 37 | * `horizontal`: Children are arranged horizontally 38 | * `grid`: Children are arranged to be equal size in a grid 39 | * `max`: One child takes up the full view of the container, other children hidden 40 | * Planned Implementation 41 | * `tabbed`: Exactly like Max, only there is a visual tab indication 42 | * `float`: Clients are floated, but bound by the container 43 | * `freefloat`: Clients are free to float, even on top of the current view 44 | 45 | ## `focus` *`brother|pc` `(-)delta`* 46 | Sets the **Focus Node**, approximating the current **Focus Node**. Good for shifting focus around. For example, to get the focus' brother next in its parent container, simply issue `focus brother 1` or to select the **Focus Node **'s parent, do a `focus pc -1`. 47 | 48 | ## `shift` *`brother|pc` `(-)delta`* 49 | Shift the current **Focus Node** in . This wraps around if you attempt to move to a node that doesn't exist. Note, shifting to a positive pc won't do anything, shift pc should only be used with a negative delta to shift a client up toward its parent. 50 | 51 | ## `containerize` 52 | If the current **Focus Node** is not an only child, containerize creates a container in place of the currrent **Focus Node**, reparent the old **Focus Node** to this container, and then focuses this child. 53 | 54 | ## `kill` 55 | Kills the **Focus Node** and any of its children. Simply issue `kill` to eliminate the **Focus Node** from the tree. 56 | 57 | ## `mark` *`markname`* 58 | Marks the current **View Node** so that it can later be `jump`'d back to. To mark the current **View Node** at any point: `mark nameOfMark`. 59 | 60 | ## `jump` *`markname`* 61 | Sets the **View Node** set by `mark` at **Mark Name**. Simple syntax of `jump markName`. 62 | -------------------------------------------------------------------------------- /src/events.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "foo-wm.h" 8 | #include "commands.h" 9 | #include "events.h" 10 | #include "tree.h" 11 | #include "window.h" 12 | #include "lookup.h" 13 | #include "util.h" 14 | 15 | 16 | void eMapRequest(XEvent *event) { 17 | fprintf(stderr, "Got a map request\n"); 18 | Node *newNode = allocateNode(); 19 | newNode -> window = event -> xmaprequest.window; 20 | 21 | /* For Click to Focus */ 22 | XGrabButton(display, AnyButton, AnyModifier, newNode -> window, 23 | True, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeSync, 24 | None, None); 25 | 26 | //Containerize and move the viewNode 27 | if (focusedNode == viewNode) { 28 | fprintf(stderr, "Focused node is equal to the viewnode\n"); 29 | containerize(); 30 | viewNode = viewNode -> parent ? viewNode -> parent : viewNode; 31 | 32 | //This is the case in which we reparented the root node 33 | if (focusedNode == rootNode) { rootNode = viewNode; } 34 | 35 | //Brother the new node and rerender 36 | brotherNode(newNode, viewNode -> child, 1); 37 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 38 | } else if (focusedNode && focusedNode -> parent) { 39 | brotherNode(newNode, focusedNode, 1); 40 | placeNode(focusedNode, 41 | focusedNode -> parent -> x, focusedNode -> parent -> y, 42 | focusedNode -> parent -> width, focusedNode -> parent -> height); 43 | } else { 44 | //No focus node, fist element created 45 | fprintf(stderr, "FIRST NODE YO\n"); 46 | 47 | parentNode(newNode, viewNode); 48 | } 49 | 50 | fprintf(stderr, "\n\nAFTA\n\n"); 51 | 52 | addLookupEntry(newNode, &newNode -> window); 53 | fprintf(stderr, "added the lookup entry\n"); 54 | focusNode(newNode, NULL, True, True); 55 | fprintf(stderr, "done with the map request\n"); 56 | } 57 | 58 | void eDestroyNotify(XEvent *event) { 59 | fprintf(stderr, "DESTROY NOTIFY RECIEVED"); 60 | fprintf(stderr, "\n\n Got here\n\n"); 61 | Node *n = getNodeByWindow(&(event -> xdestroywindow.window)); 62 | if (n == NULL) return; 63 | 64 | if (n == viewNode) { 65 | viewNode = n -> parent; 66 | fprintf(stderr, "Equals view node\n"); 67 | } 68 | destroyNode(n); 69 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 70 | } 71 | 72 | void eConfigureRequest(XEvent *e) { 73 | /* Structed From DWM */ 74 | XConfigureRequestEvent *ev = &e->xconfigurerequest; 75 | Node *configuredNode = getNodeByWindow(&ev->window); 76 | 77 | XWindowChanges wc; 78 | wc.x = configuredNode ? configuredNode -> x : ev -> x; 79 | wc.y = configuredNode ? configuredNode -> y : ev -> y; 80 | wc.width = ev->width; 81 | wc.height = ev->height; 82 | wc.border_width = ev->border_width; 83 | wc.sibling = ev->above; 84 | wc.stack_mode = ev->detail; 85 | XConfigureWindow(display, ev->window, ev->value_mask, &wc); 86 | 87 | if (configuredNode) 88 | placeNode(configuredNode, 89 | configuredNode -> x, configuredNode -> y, 90 | configuredNode -> width, configuredNode -> height); 91 | 92 | XSync(display, False); 93 | } 94 | 95 | void eResizeRequest(XEvent *event) { 96 | 97 | } 98 | 99 | void eButtonPress(XEvent *event) { 100 | fprintf(stderr, "Button Event Window is %p\n", &(event -> xbutton.window)); 101 | 102 | // Root Window 103 | if (event -> xbutton.window == None) return; 104 | 105 | // Click to Focus 106 | focusNode(getNodeByWindow(&(event -> xbutton.window)), event, True, True); 107 | } 108 | 109 | void handleXEvent(XEvent *event) { 110 | switch (event -> type) { 111 | case MapRequest: eMapRequest(event); break; 112 | case DestroyNotify: eDestroyNotify(event); break; 113 | case ConfigureRequest: eConfigureRequest(event); break; 114 | case ResizeRequest: eResizeRequest(event); break; 115 | case ButtonPress: eButtonPress(event); break; 116 | default: break; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /config/.xbindkeysrc: -------------------------------------------------------------------------------- 1 | ### Applications 2 | "urxvt" 3 | m:0x8 + c:36 4 | Alt + enter 5 | 6 | "dwb" 7 | m:0x8 + c:51 8 | Alt + backslash 9 | 10 | "xdotool getactivewindow windowkill" 11 | m:0x8 + c:24 12 | Alt + q 13 | 14 | ### WM 15 | 16 | # Layout Commands 17 | "foo-wm-c /tmp/foo-wm.socket 'layout vertical'" 18 | m:0x8 + c:55 19 | Alt + v 20 | 21 | "foo-wm-c /tmp/foo-wm.socket 'layout horizontal'" 22 | m:0x8 + c:56 23 | Alt + b 24 | 25 | "foo-wm-c /tmp/foo-wm.socket 'layout grid'" 26 | m:0x8 + c:57 27 | Alt + n 28 | 29 | "foo-wm-c /tmp/foo-wm.socket 'layout max'" 30 | m:0x8 + c:58 31 | Alt + m 32 | 33 | # Containerize Command 34 | "foo-wm-c /tmp/foo-wm.socket 'containerize'" 35 | m:0x8 + c:47 36 | Alt + semicolon 37 | 38 | # Focus Commands 39 | "foo-wm-c /tmp/foo-wm.socket 'focus brother 1'" 40 | m:0x8 + c:23 41 | Alt + Tab 42 | 43 | "foo-wm-c /tmp/foo-wm.socket 'focus brother 2'" 44 | m:0x8 + c:43 45 | Alt + h 46 | 47 | "foo-wm-c /tmp/foo-wm.socket 'focus brother 1'" 48 | m:0x8 + c:44 49 | Alt + j 50 | 51 | "foo-wm-c /tmp/foo-wm.socket 'focus brother -1'" 52 | m:0x8 + c:45 53 | Alt + k 54 | 55 | "foo-wm-c /tmp/foo-wm.socket 'focus brother -2'" 56 | m:0x8 + c:46 57 | Alt + l 58 | 59 | 60 | 61 | "foo-wm-c /tmp/foo-wm.socket 'focus pc -1'" 62 | m:0x8 + c:32 63 | Alt + o 64 | 65 | "foo-wm-c /tmp/foo-wm.socket 'focus pc 1'" 66 | m:0x8 + c:33 67 | Alt + p 68 | 69 | # Move Commands 70 | "foo-wm-c /tmp/foo-wm.socket 'move 1'" 71 | m:0x9 + c:23 72 | Shift+Alt + Tab 73 | 74 | # Shift VIM-Esque Movement 75 | "foo-wm-c /tmp/foo-wm.socket 'shift brother -2'" 76 | m:0x9 + c:43 77 | Shift+Alt + h 78 | 79 | "foo-wm-c /tmp/foo-wm.socket 'shift brother 1'" 80 | m:0x9 + c:44 81 | Shift+Alt + j 82 | 83 | 84 | "foo-wm-c /tmp/foo-wm.socket 'shift brother -1'" 85 | m:0x9 + c:45 86 | Shift+Alt + k 87 | 88 | "foo-wm-c /tmp/foo-wm.socket 'shift brother -2'" 89 | m:0x9 + c:46 90 | Shift+Alt + l 91 | 92 | 93 | "foo-wm-c /tmp/foo-wm.socket 'set client_border_width 1'" 94 | m:0x8 + c:10 95 | Alt + 1 96 | 97 | "foo-wm-c /tmp/foo-wm.socket 'set client_border_width 20'" 98 | m:0x8 + c:11 99 | Alt + 2 100 | 101 | 102 | 103 | # Zoom Commands 104 | "foo-wm-c /tmp/foo-wm.socket 'zoom -1'" 105 | m:0x8 + c:31 106 | Alt + i 107 | 108 | "foo-wm-c /tmp/foo-wm.socket 'zoom 1'" 109 | m:0x8 + c:30 110 | Alt + u 111 | 112 | 113 | # Killing Clients 114 | "foo-wm-c /tmp/foo-wm.socket 'kill client'" 115 | m:0x8 + c:24 116 | Alt + q 117 | 118 | 119 | # Dumps the Tree 120 | "foo-wm-c /tmp/foo-wm.socket 'dump tree'" 121 | m:0x8 + c:28 122 | Alt + t 123 | 124 | 125 | # Dmenu 126 | "dmenu-suggestions.rb | dmenu -h 40 -x 100 -y 400 -w 1100 -fn 'Envy Code R-16' -sb '#ffffff' -nf '#2b4f00' -nb '#cfcfcf' -sf '#0052d6' -b -p 'Command' | xargs -0 -I INPUT handler INPUT" 127 | m:0x8 + c:40 128 | Alt + d 129 | 130 | # Dmenu 131 | "cat /home/mil/Code/Github/foo-wm/config/commands | dmenu -h 40 -x 100 -y 400 -w 1100 -fn 'Envy Code R-16' -sb '#ffffff' -nf '#2b4f00' -nb '#cfcfcf' -sf '#0052d6' -b -p 'Foo-WM' | xargs -0 -I INPUT foo-wm-c /tmp/foo-wm.socket INPUT" 132 | m:0x8 + c:38 133 | Alt + a 134 | 135 | # Dmenu 136 | "find /etc/network.d -maxdepth 1 -type f -printf '%f\n' | dmenu -h 40 -x 100 -y 400 -w 1100 -fn 'Envy Code R-16' -sb '#ffffff' -nf '#2b4f00' -nb '#cfcfcf' -sf '#0052d6' -b -p 'Netcfg' | tr -d '\n' | xargs -0 -I NETWORK sudo netcfg NETWORK" 137 | m:0x8 + c:25 138 | Alt + w 139 | 140 | 141 | 142 | 143 | #Brightness controls 144 | "sudo brightcontrol down" 145 | m:0x0 + c:232 146 | XF86MonBrightnessDown 147 | 148 | "sudo brightcontrol up" 149 | m:0x0 + c:233 150 | XF86MonBrightnessDown 151 | 152 | 153 | #Multimedia 154 | "dvol -i 2" 155 | m:0x0 + c:123 156 | XF86AudioRaiseVolume 157 | 158 | "dvol -d 2" 159 | m:0x0 + c:122 160 | XF86AudioLowerVolume 161 | 162 | "dvol -t" 163 | m:0x0 + c:121 164 | XF86AudioMute 165 | 166 | "mediaControl previous" 167 | m:0x0 + c:173 168 | XF86AudioPrev 169 | 170 | "mediaControl next" 171 | m:0x0 + c:171 172 | XF86AudioNext 173 | 174 | "mediaControl pause" 175 | m:0x0 + c:172 176 | XF86AudioPlay 177 | 178 | "scrot -s" 179 | m:0x9 + c:13 180 | Shift+Alt + 4 181 | -------------------------------------------------------------------------------- /src/foo-wm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "config.h" 14 | #include "commands.h" 15 | #include "foo-wm.h" 16 | 17 | #include "atoms.h" 18 | #include "events.h" 19 | #include "tree.h" 20 | #include "util.h" 21 | #include "window.h" 22 | 23 | 24 | void handleEvents(void) { 25 | XEvent event; 26 | socklen_t returnAddressSize; 27 | int socketFd, xFd, socketReturnFd; 28 | struct sockaddr_un socketAddress, returnAddress; 29 | char commands[256]; int commandsLength; 30 | 31 | /* Setup the X FD */ 32 | xFd = ConnectionNumber(display); 33 | 34 | /* Get PID, Setup Socket Family, And Setup Address based on PID */ 35 | socketAddress.sun_family = AF_UNIX; 36 | char * socketName = SOCKET_NAME; 37 | if (!strcmp(socketName, "NONE")) { 38 | pid_t pid = getpid(); 39 | *socketName = sprintf(socketName, "foo-wm-%d.socket", pid); 40 | } 41 | sprintf(socketAddress.sun_path, "%s%s", SOCKET_PATH, socketName); 42 | unlink(socketAddress.sun_path); 43 | 44 | /* Setup Socket FD, Bind and Listen */ 45 | socketFd = socket(AF_UNIX, SOCK_STREAM, 0); 46 | bind(socketFd, (struct sockaddr *)&socketAddress, sizeof(socketAddress)); 47 | listen(socketFd, 5); 48 | 49 | /* Assemble the file descriptor set */ 50 | fd_set descriptors; 51 | for (;;) { 52 | FD_ZERO(&descriptors); 53 | FD_SET(xFd, &descriptors); 54 | FD_SET(socketFd, &descriptors); 55 | 56 | if (select(socketFd + 1, &descriptors, NULL, NULL, NULL)) { 57 | /* Anything on the Socket File Descriptor? */ 58 | if (FD_ISSET(socketFd, &descriptors)) { 59 | /* Deal with events from the socket */ 60 | socketReturnFd = accept( 61 | socketFd, 62 | (struct sockaddr*)&returnAddress, 63 | 64 | &returnAddressSize); 65 | if (socketReturnFd != -1) { 66 | if ((commandsLength = recv(socketReturnFd, commands, sizeof(commands), 0)) > 1) { 67 | commands[commandsLength] = '\0'; 68 | fprintf(stderr, "Recieved the message %s, from the socket\n", commands); 69 | char * response = handleCommand(commands); 70 | send(socketReturnFd, response, bytesUntilNull(response), 0); 71 | close(socketReturnFd); 72 | } 73 | } else { 74 | fprintf(stderr, "Error on accept getting FD: %s\n", strerror(errno)); 75 | } 76 | } 77 | 78 | /* Anything on the X File Descriptor? */ 79 | if (FD_ISSET(xFd, &descriptors)) { 80 | while (XPending(display)) { 81 | XNextEvent(display, &event); 82 | handleXEvent(&event); 83 | } 84 | } 85 | 86 | 87 | } 88 | } 89 | close(socketFd); 90 | } 91 | 92 | void setup(void) { 93 | // Open display, set screen, set root, and select root input 94 | assert((display = XOpenDisplay(NULL))); 95 | activeScreen = DefaultScreen(display); 96 | root = RootWindow(display, activeScreen); 97 | XSelectInput(display, root, SubstructureRedirectMask | SubstructureNotifyMask); 98 | setCursor(&root, 68); 99 | setupAtoms(); 100 | 101 | // Setup Defaults from config.h 102 | defaultLayout = CONTAINER_DEFAULT_LAYOUT; 103 | containerPadding = CONTAINER_PADDING; 104 | clientPadding = CLIENT_PADDING; 105 | border = CLIENT_BORDER_WIDTH; 106 | screenPaddingLeft = SCREEN_PADDING_LEFT; 107 | screenPaddingTop = SCREEN_PADDING_TOP; 108 | screenPaddingRight = SCREEN_PADDING_RIGHT; 109 | screenPaddingBottom = SCREEN_PADDING_BOTTOM; 110 | activeFocusedColor = getColor(CLIENT_ACTIVE_FOCUSED_COLOR); 111 | activeUnfocusedColor = getColor(CLIENT_ACTIVE_UNFOCUSED_COLOR); 112 | inactiveFocusedColor = getColor(CLIENT_INACTIVE_FOCUSED_COLOR); 113 | inactiveUnfocusedColor = getColor(CLIENT_INACTIVE_UNFOCUSED_COLOR); 114 | 115 | // Setup the Root Node (top of tree) 116 | rootNode = allocateNode(); 117 | rootNode -> layout = defaultLayout; 118 | viewNode = rootNode; 119 | recalculateRootDimensions(); 120 | 121 | // Set Error Handlers and Flush to X 122 | XSetErrorHandler((XErrorHandler)(xError)); 123 | XFlush(display); 124 | } 125 | 126 | 127 | int main(void) { 128 | setup(); 129 | handleEvents(); 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /src/commands.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "foo-wm.h" 5 | #include "commands.h" 6 | #include "lookup.h" 7 | #include "responses.h" 8 | #include "tree.h" 9 | #include "util.h" 10 | 11 | /* ----------------------------------------------------------------------------- 12 | * Handeling Commands 13 | * ---------------------------------------------------------------------------*/ 14 | char * handleCommand(char * request) { 15 | fprintf(stderr, "Recv from Socket: %s", request); 16 | 17 | char *tokens[5]; char *token; int i = 0; 18 | while ((token = nextToken(&request))) { 19 | tokens[i] = token; 20 | i++; 21 | } 22 | 23 | char * response = "Some arbitrary response back to the socket\0"; 24 | 25 | if (!strcmp(tokens[0], "dimensions")) 26 | dimensions(tokens[1], tokens[2]); 27 | 28 | 29 | if (!strcmp(tokens[0], "absorb")) 30 | absorb(tokens[1], tokens[2]); 31 | if (!strcmp(tokens[0], "containerize")) 32 | containerize(); 33 | else if (!strcmp(tokens[0], "focus")) 34 | focus(tokens[1], tokens[2]); 35 | else if (!strcmp(tokens[0], "get")) 36 | response = get(tokens[1]); 37 | else if(!strcmp(tokens[0], "jump")) 38 | jump(tokens[1]); 39 | else if (!strcmp(tokens[0], "kill")) 40 | kill(); 41 | else if (!strcmp(tokens[0], "layout")) 42 | layout(tokens[1]); 43 | else if (!strcmp(tokens[0], "mark")) 44 | mark(tokens[1]); 45 | else if (!strcmp(tokens[0], "set")) 46 | set(tokens[1], tokens[2]); 47 | else if (!strcmp(tokens[0], "shift")) 48 | shift(tokens[1], atoi(tokens[2])); 49 | else if (!strcmp(tokens[0], "swap")) 50 | swap(tokens[1], tokens[2]); 51 | else if (!strcmp(tokens[0], "zoom")) 52 | zoom(atoi(tokens[1])); 53 | 54 | XFlush(display); 55 | return response; 56 | } 57 | 58 | 59 | /* Extracts the next token from the tokenString */ 60 | char * nextToken(char ** tokenString) { 61 | char *command; 62 | command = strsep(tokenString, " "); 63 | if (!command) return NULL; 64 | 65 | char *newLine = strchr(command, '\n'); 66 | if (newLine) *newLine = '\0'; 67 | 68 | return command; 69 | } 70 | 71 | 72 | /* ----------------------------------------------------------------------------- 73 | * IPC Commands 74 | * ---------------------------------------------------------------------------*/ 75 | void dimensions(char * w, char * h) { 76 | int width = atoi(w); 77 | int height = atoi(h); 78 | rootNode -> x = rootX = screenPaddingLeft; 79 | rootNode -> y = rootY = screenPaddingTop; 80 | rootNode -> width = rootWidth = width - screenPaddingLeft - screenPaddingRight; 81 | rootNode -> height = rootHeight = height - screenPaddingTop - screenPaddingBottom; 82 | } 83 | 84 | 85 | void absorb(char * argA, char * argB) { 86 | /* Absorbs the given node into the container of the focused node 87 | * If the focused node is a client, containerize will be called then absorb 88 | * */ 89 | if (!focusedNode) return; 90 | 91 | if (isClient(focusedNode)) containerize(); 92 | 93 | } 94 | 95 | void containerize(void) { 96 | if (!focusedNode) return; 97 | if (focusedNode -> parent && focusedNode -> parent -> child && !isClient(focusedNode -> parent)) 98 | if (isOnlyChild(focusedNode)) return; 99 | 100 | Node *insertNode, *newContainer = allocateNode(); int insertPosition; 101 | if (focusedNode -> parent && focusedNode -> parent -> focus == focusedNode) 102 | focusedNode -> parent -> focus = newContainer; 103 | 104 | if (focusedNode -> previous) { 105 | insertNode = focusedNode -> previous; insertPosition = NEXT; 106 | } else { 107 | insertNode = focusedNode -> next; insertPosition = PREVIOUS; 108 | } 109 | 110 | parentNode(focusedNode, newContainer); 111 | brotherNode(newContainer, insertNode, insertPosition); 112 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 113 | } 114 | 115 | 116 | void focus(char * argA, char * argB) { 117 | if (!focusedNode) return; 118 | 119 | int delta = atoi(argB); 120 | Node * newFocus = NULL; 121 | 122 | if (!strcmp(argA, "id")) { 123 | newFocus = getNodeById(delta); 124 | 125 | } else if (!strcmp(argA, "brother")) { 126 | newFocus = getBrother(focusedNode, delta); 127 | } else if (!strcmp(argA, "pc")) { 128 | while (delta != 0) { 129 | newFocus = (delta < 0) ? 130 | focusedNode -> parent : focusOrChildOf(focusedNode); 131 | delta = delta + ( delta > 0 ? -1 : 1); 132 | focusNode(newFocus, NULL, True, True); 133 | } return; 134 | } 135 | 136 | focusNode(newFocus, NULL, True, True); 137 | } 138 | 139 | char * get(char * property) { 140 | if (!strcmp(property, "tree")) 141 | return jsonTree(rootNode, 0); 142 | else if (!strcmp(property, "view")) 143 | return jsonTree(viewNode, 0); 144 | else if (!strcmp(property, "focus")) 145 | return jsonTree(focusedNode, 0); 146 | else if (!strcmp(property, "marks")) 147 | return jsonMarks(); 148 | 149 | return ""; 150 | } 151 | 152 | void kill(void) { 153 | fprintf(stderr, "Destroying Client %p\n", focusedNode); 154 | 155 | if (isClient(focusedNode)) { 156 | 157 | /* Save closest client and destroy node */ 158 | Node *newFocus = getClosestClient(focusedNode); 159 | 160 | if (focusedNode == viewNode) viewNode = viewNode -> parent; 161 | 162 | if ( isOnlyChild(focusedNode) && focusedNode -> parent) { 163 | viewNode = focusedNode -> parent -> parent ? 164 | focusedNode -> parent -> parent : focusedNode -> parent; 165 | } 166 | 167 | Node *oldFocus = focusedNode; 168 | focusNode(newFocus, NULL, True, True); 169 | destroyNode(oldFocus); 170 | 171 | rePlaceNode(viewNode); 172 | } 173 | } 174 | 175 | void jump(char * markName) { 176 | fprintf(stderr, "Finding a node"); 177 | 178 | Mark *n = NULL; 179 | for(n = markTail; n; n = n -> previous) { 180 | if (!strcmp(n -> name, markName)) { 181 | fprintf(stderr, "Going to focus mark %p", n -> node); 182 | unmapNode(viewNode); 183 | viewNode = n -> node; 184 | placeNode(n -> node, rootX, rootY, rootWidth, rootHeight); 185 | focusNode(n -> node, NULL, True, True); 186 | while (!isClient(focusedNode)) focus("pc", "1"); 187 | } 188 | } 189 | } 190 | 191 | 192 | void layout(char * l) { 193 | fprintf(stderr, "Setting layout to: %s", l); 194 | 195 | Node *setNode = isClient(focusedNode) ? focusedNode -> parent : focusedNode; 196 | int newLayout = 0; 197 | 198 | if (!strcmp(l, "vertical")) newLayout = VERTICAL; 199 | else if (!strcmp(l, "horizontal")) newLayout = HORIZONTAL; 200 | else if (!strcmp(l, "grid")) newLayout = GRID; 201 | else if (!strcmp(l, "max")) newLayout = MAX; 202 | else if (!strcmp(l, "float")) newLayout = FLOAT; 203 | 204 | 205 | setNode -> layout = newLayout; 206 | rePlaceNode(setNode); 207 | } 208 | 209 | 210 | /* Adds the current viewNode as a mark */ 211 | void mark(char * markName) { 212 | 213 | Mark *m = malloc(sizeof(Mark)); 214 | m -> name = malloc(sizeof(markName)); 215 | strcpy(m -> name, markName); 216 | m -> node = viewNode; 217 | m -> previous = markTail; 218 | markTail = m; 219 | 220 | fprintf(stderr, "\nAdded the mark:: %s // %s\n", markName, markTail -> name); 221 | } 222 | 223 | void set(char * property, char * value) { 224 | 225 | if (!strcmp(property, "client_border_width")) { 226 | border = atoi(value); 227 | } else if (!strcmp(property, "container_padding")) { 228 | containerPadding = atoi(value); 229 | } else if (!strcmp(property, "client_padding")) { 230 | clientPadding = atoi(value); 231 | } else if (!strcmp(property, "screen_padding_top")) { 232 | screenPaddingTop = atoi(value); 233 | } else if (!strcmp(property, "screen_padding_left")) { 234 | screenPaddingLeft = atoi(value); 235 | } else if (!strcmp(property, "screen_padding_right")) { 236 | screenPaddingRight = atoi(value); 237 | } else if (!strcmp(property, "screen_padding_bottom")) { 238 | screenPaddingBottom = atoi(value); 239 | } 240 | 241 | recalculateRootDimensions(); 242 | rePlaceNode(viewNode); 243 | 244 | } 245 | 246 | //Moves the current selection given amount 247 | void shift(char * argA, int delta) { 248 | 249 | int brotherSwitch = -1; 250 | if (!strcmp(argA, "brother")) brotherSwitch = 1; 251 | else if (!strcmp(argA, "pc")) brotherSwitch = 0; 252 | else return; 253 | 254 | while (delta != 0) { 255 | if (brotherSwitch) { 256 | Node *swapNode = getBrother(focusedNode, delta); 257 | swapNodes(focusedNode, swapNode); 258 | focusNode(focusedNode, NULL, True, True); 259 | delta = 0; 260 | } else { 261 | if (delta > 0) { return; } else { 262 | if (focusedNode -> parent && focusedNode -> parent -> parent) { 263 | Node *newParent = focusedNode -> parent -> parent; 264 | parentNode(focusedNode, newParent); 265 | rePlaceNode(newParent); 266 | } else { return; } 267 | delta++; 268 | } 269 | } 270 | } 271 | 272 | } 273 | 274 | /* Swaps two nodes in place based on node ids */ 275 | Bool swap(char * argA , char * argB) { 276 | int idA = atoi(argA), 277 | idB = atoi(argB); 278 | Node *nodeA = getNodeById(idA), 279 | *nodeB = getNodeById(idB); 280 | 281 | if (!nodeA || !nodeB) { 282 | return False; 283 | } else { 284 | swapNodes(nodeA, nodeB); 285 | return True; 286 | } 287 | } 288 | 289 | 290 | /* Updates the viewNode approximating the current focusNode */ 291 | void zoom(int level) { 292 | while (level < 0) { 293 | if (viewNode -> parent) { 294 | unmapNode(viewNode); 295 | viewNode = viewNode -> parent; 296 | } else { return; } 297 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 298 | focusNode(focusedNode, NULL, True, True); 299 | level++; 300 | } 301 | while (level > 0) { 302 | if (focusedNode == viewNode) return; 303 | Node *n = focusedNode; 304 | while (n && n -> parent != viewNode) n = n -> parent; 305 | if (!n) return; 306 | 307 | unmapNode(viewNode); 308 | viewNode = n; 309 | //TODO:: Need placeNode to be aware of focusnnode (in)active colors.... 310 | 311 | fprintf(stderr, "placeing yo"); 312 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 313 | if (focusedNode == viewNode && !isClient(focusedNode)) { 314 | focusNode(focusOrChildOf(focusedNode), NULL, True, True); 315 | } 316 | level--; 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /src/tree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "atoms.h" 7 | #include "foo-wm.h" 8 | #include "tree.h" 9 | #include "lookup.h" 10 | #include "util.h" 11 | #include "window.h" 12 | 13 | /* -------------------------------------------------------------------------- 14 | * Bool Returns 15 | * -------------------------------------------------------------------------- */ 16 | Bool areBrothers(Node * nodeA, Node * nodeB) { 17 | if ((!nodeA || !nodeB) || nodeA -> parent != nodeB -> parent) return False; 18 | 19 | Bool hasA, hasB; 20 | Node *n = NULL; 21 | for (n = nodeA -> parent -> child; n; n = n -> next) { 22 | if (n == nodeA) hasA = True; 23 | if (n == nodeB) hasB = True; 24 | } 25 | 26 | if (hasA && hasB) return True; 27 | else return False; 28 | } 29 | 30 | Bool isClient(Node * node) { 31 | /* Is the node a client? */ 32 | if (node && (node -> window != (Window) NULL)) return True; 33 | else return False; 34 | } 35 | 36 | Bool isOnlyChild(Node * node) { 37 | /* Is the node an only child */ 38 | if (node && (node -> next || node -> previous)) return False; 39 | else return True; 40 | } 41 | 42 | Bool nodeIsParentOf(Node * nodeA, Node * nodeB) { 43 | /* Searches nodeA for an occurance of nodeB 44 | * if successful, return true */ 45 | if (nodeA == nodeB) return True; 46 | 47 | Node *n = NULL; 48 | for (n = nodeA -> child; n; n = n -> next) { 49 | if (nodeIsParentOf(n, nodeB)) 50 | return True; 51 | } 52 | 53 | return False; 54 | } 55 | 56 | Bool unfocusNode(Node * n, Bool focusPath) { 57 | /* Unfocuses the currently focused node, called only by focusNode 58 | * Returns Bool if an update of the view is needed 59 | * Dangerous if called alone */ 60 | if (!n) return False; 61 | 62 | Bool setView = (n == viewNode) ? True : False; 63 | fprintf(stderr, "Yo i be unfocusing %p\n", n); 64 | 65 | //Unfocusing Code for previous focusedNode 66 | if (isClient(n)) { 67 | 68 | //This should only apply to the most innard focus of focusedNode, follow ptrs 69 | if (focusPath) 70 | XGrabButton(display, AnyButton, AnyModifier, 71 | n -> window, True, ButtonPressMask | ButtonReleaseMask, 72 | GrabModeAsync, GrabModeAsync, None, None); 73 | 74 | } else { 75 | //Recursive loop on children to set 76 | 77 | Node *c; 78 | for (c = n -> child; c; c = c -> next) 79 | unfocusNode(c, c -> parent -> focus == c ? True : False); 80 | } 81 | 82 | return setView; 83 | } 84 | 85 | /* -------------------------------------------------------------------------- 86 | * Long Returns 87 | * -------------------------------------------------------------------------- */ 88 | long getBorderColor(Node * node, Bool focusPath) { 89 | if (focusPath) { 90 | if (focusedNode == node) 91 | return activeFocusedColor; 92 | else 93 | return node -> parent -> focus == node ? 94 | inactiveFocusedColor : activeUnfocusedColor; 95 | 96 | } else { 97 | return node -> parent -> focus == node ? 98 | inactiveFocusedColor: inactiveUnfocusedColor; 99 | } 100 | } 101 | 102 | 103 | /* -------------------------------------------------------------------------- 104 | * Node Returns 105 | * -------------------------------------------------------------------------- */ 106 | Node * getBrother(Node * node, int delta) { 107 | if (!node) return NULL; 108 | 109 | while (delta > 0) { 110 | if (node -> next) 111 | node = node -> next; 112 | else if (node -> parent && node -> parent -> child) 113 | node = node -> parent -> child; 114 | delta--; 115 | } 116 | 117 | while (delta < 0) { 118 | if (node -> previous) { 119 | node = node -> previous; 120 | } else if (node -> parent && node -> parent -> child) { 121 | node = node -> parent -> child; 122 | while (node -> next) 123 | node = node -> next; 124 | } else { fprintf(stderr, "Not a good situation\n"); } 125 | 126 | delta++; 127 | } 128 | 129 | return node; 130 | } 131 | 132 | /* Gets the next brother client to node, in given direction 133 | * [Container] - [Client X] - [Container] - [Container] - [Client Y] 134 | * Given Client X, function would loop until hitting Client Y 135 | * */ 136 | Node * getBrotherClient(Node * node, int direction) { 137 | Node *pNode = node; 138 | Node *nNode = node; 139 | 140 | while (pNode -> previous || nNode -> next) { 141 | if (pNode -> previous ) pNode = pNode -> previous; 142 | if (nNode -> next ) nNode = nNode -> next; 143 | switch (direction) { 144 | case 0: 145 | if (isClient(pNode) && pNode != node) return pNode; 146 | if (isClient(nNode) && nNode != node) return nNode; 147 | break; 148 | case 1: 149 | if (isClient(nNode) && nNode != node) return nNode; 150 | if (isClient(pNode) && pNode != node) return pNode; 151 | break; 152 | } 153 | } 154 | return NULL; 155 | } 156 | 157 | 158 | Node * getClosestClient(Node * node) { 159 | Node * returnNode = NULL; 160 | Node * currentNode = node; 161 | 162 | /* Calls getBrotherClient going up the tree until a client is found */ 163 | while (!returnNode) { 164 | returnNode = getBrotherClient(currentNode, 1); 165 | if (!returnNode) { 166 | if (currentNode -> parent) currentNode = currentNode -> parent; 167 | else return NULL; 168 | } else { //We found a client 169 | return returnNode; 170 | } 171 | } 172 | return NULL; 173 | } 174 | 175 | 176 | 177 | /* -------------------------------------------------------------------------- 178 | * Void Returns 179 | * -------------------------------------------------------------------------- */ 180 | void brotherNode(Node *node, Node * brother, int position) { 181 | if (!node || !brother) return; 182 | node -> parent = brother -> parent; 183 | 184 | if (position == 0) { 185 | node -> next = brother; 186 | if (!brother -> previous) { //Pop in the front 187 | node -> parent -> child = node; 188 | } else { 189 | //Shift previous pointer 190 | node -> previous = brother -> previous; 191 | if (node -> previous) node -> previous -> next = node; 192 | } 193 | brother -> previous = node; 194 | } else if (position == 1) { 195 | node -> previous = brother; 196 | node -> next = brother -> next; 197 | if (node -> next) node -> next -> previous = node; 198 | brother -> next = node; 199 | } 200 | } 201 | 202 | 203 | void destroyNode(Node * n) { 204 | if (!n) return; 205 | 206 | //Recursvily unmap up any lone parents 207 | if (n -> parent && n -> parent != viewNode && isOnlyChild(n) && 208 | n -> parent -> child == n && n -> parent -> parent) { 209 | destroyNode(n -> parent); 210 | return; 211 | } 212 | 213 | //Unparent the node 214 | unparentNode(n); 215 | fprintf(stderr, "Made it here"); 216 | 217 | if (n == focusedNode) focusedNode = NULL; 218 | fprintf(stderr, "n is %p", n); 219 | 220 | //Recursivly unmap down all children of the node 221 | if (isClient(n)) { 222 | removeLookupEntry(&n -> window); 223 | 224 | //Send ICCCM Delete Atom 225 | sendDeleteWindow(&n -> window); 226 | 227 | 228 | XDestroyWindow(display, n -> window); 229 | free(n); 230 | } else if (n) { 231 | Node *destroy = n -> child; Node *next = NULL; 232 | do { 233 | if (destroy) { 234 | next = destroy -> next; 235 | destroyNode(destroy); 236 | } else { next = NULL; } 237 | } while (next); 238 | 239 | //if (n -> parent && n -> parent -> focus == n) n -> parent -> child = NULL; 240 | //free(n); 241 | } 242 | 243 | } 244 | 245 | 246 | //This should focus OR select 247 | void focusNode(Node * n, XEvent * event, Bool setFocused, Bool focusPath) { 248 | if (!n || n == focusedNode) return; 249 | fprintf(stderr, "Focusing %p", n); 250 | 251 | Node *oldFocus = focusedNode; 252 | 253 | 254 | /* Focus path and set focus --> Update the focus ptr of parent */ 255 | if (focusPath && setFocused) { 256 | fprintf(stderr, "\n\nNode %p, is in the focus path\n\n", n); 257 | unfocusNode(focusedNode, True); 258 | if (setFocused && n -> parent) n -> parent -> focus = n; 259 | } 260 | 261 | /* Setting focus */ 262 | if (setFocused) { 263 | focusedNode = n; 264 | 265 | if (oldFocus && nodeIsParentOf(viewNode, oldFocus)) 266 | rePlaceNode(oldFocus); 267 | if (oldFocus == viewNode && nodeIsParentOf(focusedNode, viewNode)) 268 | viewNode = n; 269 | if (areBrothers(oldFocus, focusedNode)) 270 | placeNode(focusedNode, oldFocus -> x, oldFocus -> y, oldFocus -> width, oldFocus -> height); 271 | 272 | placeNode(viewNode, rootX, rootY, rootWidth, rootHeight); 273 | 274 | } 275 | 276 | // Are we at the bottom level 277 | if (isClient(n)) { 278 | if (focusPath) { 279 | XSetInputFocus(display, n -> window, RevertToParent, CurrentTime); 280 | XUngrabButton(display, AnyButton, AnyModifier, n ->window); 281 | XRaiseWindow(display, n -> window); 282 | 283 | if (event) { 284 | // Set the Input focus, and ungrab the window (no longer point to click) 285 | XSendEvent(display, n -> window, True, ButtonPressMask, event); 286 | } else { 287 | centerPointer(&n -> window); 288 | } 289 | } 290 | 291 | } else { 292 | /* Focus on container -- recur down focus path */ 293 | Node *i = NULL; 294 | for (i = n -> child; i; i = i -> next) { 295 | focusNode(i, NULL, False, 296 | i -> parent -> focus == i ? True : False); 297 | } 298 | } 299 | } 300 | 301 | 302 | void parentNode(Node *node, Node *parent) { 303 | fprintf(stderr, "Pareting node %p into parent %p\n", node, parent); 304 | if (!node || !parent) return; //Cant add to NULL 305 | 306 | unparentNode(node); //Unparent then set the parent to new parent 307 | node -> parent = parent; 308 | if (!parent -> focus) parent -> focus = node; 309 | 310 | //Find last in children of parent, add to end 311 | if (parent -> child) { 312 | Node *n = parent -> child; 313 | while (n -> next) n = n -> next; 314 | node -> previous = n; 315 | n -> next = node; 316 | } else { 317 | parent -> child = node; 318 | } 319 | } 320 | 321 | void placeNode(Node * node, int x, int y, int width, int height) { 322 | if (!node) return; 323 | node -> x = x; node -> y = y; node -> width = width; node -> height = height; 324 | fprintf(stderr, "Place Node XY:[%d, %d], WH:[%d, %d]\n", x, y, width, height); 325 | 326 | if (isClient(node)) { 327 | fprintf(stderr,"Rendering window\n"); 328 | XMapWindow(display, node -> window); 329 | XRaiseWindow(display, node -> window); 330 | XMoveResizeWindow(display, node -> window, 331 | (x < 0) ? 0 : x, (y < 0) ? 0 : y, 332 | (width - (border * 2)) > 0 ? (width - border * 2) : 1, 333 | (height - (border * 2)) > 0 ? (height- border * 2) : 1); 334 | XSetWindowBorderWidth(display, node -> window, border); 335 | 336 | Node *b = node; Bool inFocusPath = False; 337 | 338 | if (b == focusedNode) { 339 | inFocusPath = True; 340 | } else { 341 | do { //Figure out if were in the focus path 342 | b = b -> parent; 343 | if (b == focusedNode) inFocusPath = True; 344 | } while (b -> parent); 345 | } 346 | 347 | XSetWindowBorder(display, node -> window, getBorderColor(node, inFocusPath)); 348 | 349 | } else { 350 | //Count up children prior to loop 351 | int children = 0; int i = 0; Node *a = NULL; 352 | if (!node -> child) return; 353 | for (a = node -> child; a; a = a -> next) children++; 354 | 355 | /* Determine the number of rows and cols */ 356 | int rows; int cols; 357 | switch (node -> layout) { 358 | /* Emulate a grid regardless */ 359 | case VERTICAL : cols = children; rows = 1; break; 360 | case HORIZONTAL: cols = 1; rows = children; break; 361 | case GRID : gridDimensions(children, &rows, &cols); break; 362 | case MAX : cols = 1; rows = 1; break; 363 | } 364 | 365 | Bool callPlace; 366 | int pad; 367 | for (a = node -> child; a; a = a -> next, i++) { 368 | 369 | if (node -> layout == FLOAT) { 370 | placeNode(a, a -> x + 10, a -> y + 10 , a -> width - 20, a -> height - 20); 371 | 372 | } else { /* Rendering based on a grid style */ 373 | 374 | pad = isClient(a) ? clientPadding : containerPadding; 375 | callPlace = True; 376 | if (node -> layout == MAX) { 377 | if (a -> parent -> focus == a) i = 0; 378 | else callPlace = False; 379 | } 380 | 381 | a -> x = x + (i % cols) * (width/cols) + pad; 382 | a -> y = y + ((int)(i / cols)) * (height/rows) + pad; 383 | a -> width = width / cols - (pad * 2); 384 | a -> height = height / rows - (pad * 2); 385 | 386 | if (callPlace) { 387 | 388 | if (node -> layout == GRID) { /* Must account for prime case */ 389 | if (children == 2) a -> height = height - (pad * 2); 390 | if (i + 1 == children) a -> width = x + width - a -> x - (pad * 2); 391 | } 392 | rePlaceNode(a); 393 | } else { 394 | fprintf(stderr, "Going to call unmap on %p\n", a); 395 | unmapNode(a); 396 | } 397 | 398 | } 399 | } 400 | } 401 | } 402 | 403 | void rePlaceNode(Node * node) { 404 | placeNode(node, node -> x, node -> y, node -> width, node -> height); 405 | } 406 | 407 | /* Swaps nodes within the same container of the tree 408 | * [ NULL <- A <-> B <-> C <-> D -> NULL ] */ 409 | void swapNodes(Node * a, Node * b) { 410 | if (!a || !b || a == b) return; 411 | 412 | /* Update Parent / Parent -> Child Pointer */ 413 | Node *temp = NULL; 414 | if (a -> parent -> child == a) a -> parent -> child = b; 415 | else if (b -> parent -> child == b) b -> parent -> child = a; 416 | 417 | /* Update Previous Pointer */ 418 | temp = a -> previous; a -> previous = b -> previous; 419 | if (a -> previous) a -> previous -> next = a; 420 | b -> previous = temp; 421 | if (b -> previous) b -> previous -> next = b; 422 | 423 | /* Update Next Pointer */ 424 | temp = a -> next; a -> next = b -> next; 425 | if (a -> next) a -> next -> previous = a; 426 | b -> next = temp; 427 | if (b -> next) b -> next -> previous = b; 428 | 429 | /* Replace node */ 430 | placeNode(viewNode, viewNode -> x, viewNode -> y, 431 | viewNode -> width, viewNode -> height); 432 | } 433 | 434 | 435 | void unmapNode(Node * node) { 436 | if (isClient(node)) { 437 | XUnmapWindow(display, node -> window); 438 | } else { 439 | Node *n = NULL; 440 | for (n = node -> child; n; n = n -> next) 441 | unmapNode(n); 442 | } 443 | } 444 | 445 | 446 | void unparentNode(Node *node) { 447 | if (!(node && node -> parent)) return; 448 | 449 | fprintf(stderr, "Unparent called\n"); 450 | //Move parent's child pointer if were it.... 451 | if (node -> parent -> child == node) 452 | node -> parent -> child = node -> next; 453 | if (node -> parent -> focus == node) 454 | node -> parent -> focus = node -> parent -> child; 455 | 456 | //Move the next and previous pointers to cut out the node 457 | if (node -> next) node -> next -> previous = node -> previous; 458 | if (node -> previous) node -> previous -> next = node -> next; 459 | 460 | //Set our parent to NULL 461 | Node * oldParent = node -> parent; 462 | node -> parent = NULL; node -> next = NULL; node -> previous = NULL; 463 | if (!oldParent -> child) 464 | destroyNode(oldParent); 465 | } 466 | --------------------------------------------------------------------------------