commit 9e980276737f50bcb880077e536c7d5a79a0b32b Author: Marc Lagrange Date: Sun Aug 10 17:37:24 2008 +0200 Initial commit - New improvments : · Configuration with ~/.wmfsrc with libconfig · Bugfixes · Layouts and tile · "virtuals desktops" · Bugs added :D Signed-off-by: Marc Lagrange diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ebaa5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +*.o +.*.sw? +wmfs +config.h +CHANGELOG +config.h +#* +\#* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5e050c1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,80 @@ +#CMakeLists.txt + +# Minimum version of CMake +cmake_minimum_required(VERSION 2.6) +if(COMMAND cmake_policy) + cmake_policy(VERSION 2.6) +endif() +# Source dir +set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) +# Project name - wmfs +project(wmfs C) +# Definition of the wmfs source +set(wmfs_src + wmfs.c + config.c) + +# Set the executable from the wmfs_src +add_executable(wmfs ${wmfs_src}) +# Set the version - NOT USED AT THE MOMENT +set(VERSION wmfs-devel) +# CFLAGS +set(CFLAGS "-g -Wall") +set(CMAKE_C_FLAGS ${CFLAGS}) +# Link Libraries +set(LIBRARIES_TO_LINK + X11 + confuse) +target_link_libraries(wmfs ${LIBRARIES_TO_LINK}) + +# Messages +message("Project version : ${VERSION}") +message("Using these CFLAGS : ${CFLAGS}") +message("Linking with theses libraries : ${LIBRARIES_TO_LINK}") + +# Include pkg-config +include(FindPkgConfig) +# Use pkgconfig to get required libraries +pkg_check_modules(wmfs_required + x11 + xcb + libconfuse) +# Find exterbal programs +find_program(GIT_EXECUTABLE git) +# Remplace strings in configs +set(WMFS_VERSION ${VERSION}) +set(WMFS_COMPILE_MACHINE ${CMAKE_SYSTEM_PROCESSOR}) +set(WMFS_COMPILE_BY $ENV{USER}) +set(WMFS_COMPILE_FLAGS ${CFLAGS}) +set(WMFS_LINKED_LIBS ${LIBRARIES_TO_LINK}) +# Configure files +set(wmfs_configure_files + config.h.in) + +macro(a_configure_file file) + string(REGEX REPLACE ".in\$" "" outfile ${file}) + message(STATUS "Configuring ${outfile}") + configure_file(${SOURCE_DIR}/${file} + ${BUILD_DIR}/${outfile} + ESCAPE_QUOTE + @ONLY) +endmacro() + +foreach(file ${wmfs_configure_files}) + a_configure_file(${file}) +endforeach() + +# Generating CHANGELOG + +if(EXISTS ${SOURCE_DIR}/.git/HEAD AND GIT_EXECUTABLE) + # generate +execute_process( + COMMAND ${GIT_EXECUTABLE} log + WORKING_DIRECTORY ${SOURCE_DIR} + #COMMENT "Generating CHANGELOG" + OUTPUT_VARIABLE WMFS_CHANGELOG + ) +set(CHANGELOG_FILE ${SOURCE_DIR}/CHANGELOG) +file(WRITE ${CHANGELOG_FILE} ${WMFS_CHANGELOG}) +endif() diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..aa87249 --- /dev/null +++ b/COPYING @@ -0,0 +1,28 @@ + + Copyright (c) 1983 The Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + diff --git a/README b/README new file mode 100755 index 0000000..c963e72 --- /dev/null +++ b/README @@ -0,0 +1,10 @@ +WMFS for GNU/Linux + * Window Manager From Scratch + +Un WM tout bete, crée a partir de 0. + +Auteurs: + * Code : Martin Duquesnoy + * Build System & other : Marc Lagrange + +License: BSD diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..184083c --- /dev/null +++ b/config.h.in @@ -0,0 +1,14 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include "local.h" + +#define WMFS_VERSION "@WMFS_VERSION@" +#define WMFS_COMPILE_MACHINE "@WMFS_COMPILE_MACHINE@" +#define WMFS_COMPILE_BY "@WMFS_COMPILE_BY@" +#define WMFS_COMPILE_FLAGS "@WMFS_COMPILE_FLAGS@" +#define WMFS_LINKED_LIBS "@WMFS_LINKED_LIBS@" + +void init_conf(void); + +#endif /* CONFIG_H */ diff --git a/local.h b/local.h new file mode 100644 index 0000000..bdc9f4f --- /dev/null +++ b/local.h @@ -0,0 +1,163 @@ +#ifndef LOCAL_H +#define LOCAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +/* DEFINE TYPES */ +#define FALSE 0 +#define TRUE (!FALSE) +#define ButtonMask (ButtonPressMask | ButtonReleaseMask) +#define MouseMask (ButtonMask | PointerMotionMask) +#define KeyMask (KeyPressMask | KeyReleaseMask) +#define CONTROL ControlMask +#define ALT Mod1Mask +#define SHIFT ShiftMask +#define LEN(x) (sizeof x / sizeof x[0]) +#define Move 0 +#define Resize 1 +#define Free 0 +#define Tile 1 +#define Max 2 +#define MAXTAG 36 + +typedef struct Client Client; +struct Client { + char *title; /* client title */ + int tag; /* tag num */ + int x, y, w, h; /* window attribute */ + int ox, oy, ow, oh; /* old window attribute */ + int border; /* border height */ + Window win; /* window */ + Window tbar; /* Titlebar? */ + Window button; /* Close Button */ + Bool max; /* client info */ + int layout; + Client *next; /* next client */ + Client *prev; /* previous client */ +}; + +typedef struct { + unsigned long mod; + KeySym keysym; + void (*func)(char *cmd); + char *cmd; +} Key; + +typedef struct { + + char *name; + void *func; + +} func_name_list_t; + +typedef struct { + /* bool and size */ + char *font; + bool raisefocus; + bool raiseswitch; + int borderheight; + int ttbarheight; + /* color */ + int bordernormal; + int borderfocus; + int barcolor; + int buttoncolor; + int textcolor; + int tagselfg; + int tagselbg; + /* layout */ + char *symlayout[3]; + /* tag */ + int ntag; + char *taglist[MAXTAG]; +} Conf; + +enum { CurNormal, CurResize, CurMove, CurInput, CurLast }; +enum { WMState, WMProtocols, WMName, WMDelete, WMLast }; +enum { NetSupported, NetWMName, NetLast }; + +/* wmfs.c */ +void attach(Client *c); +int clientpertag(int tag); +void detach(Client *c); +void *emallocz(unsigned int size); +int errorhandler(Display *d, XErrorEvent *event); +void focus(Client *c); +Client* getbutton(Window w); +Client* getclient(Window w); +Client* getnext(Client *c); +Client* gettbar(Window w); +void getevent(void); +void grabbuttons(Client *c, Bool focused); +void grabkeys(void); +void hide(Client *c); +void init(void); +Bool ishide(Client *c); +void keymovex(char *cmd); +void keymovey(char *cmd); +void keypress(XEvent *e); +void keyresize(char *cmd); +void killclient(char *cmd); +void mapclient(Client *c); +void manage(Window w, XWindowAttributes *wa); +void mouseaction(Client *c, int x, int y, int type); +void moveresize(Client *c, int x, int y, int w, int h); +void raiseclient(Client *c); +void scan(void); +void setborder(Window win, int color); +void spawn(char *cmd); +void tag(char *cmd); +void tagn(int tag); +void tagswitch(char *cmd); +void tile(char *cmd); +void togglemax(char *cmd); +void unhide(Client *c); +void unmanage(Client *c); +void updatebar(void); +void unmapclient(Client *c); +void updateall(void); +void updatetitle(Client *c); +void wswitch(char *cmd); + +#define BUTY(y) (y - conf.ttbarheight + 3) +#define BUTH (conf.ttbarheight - 6) + +GC gc; +Key keys[1024]; +XEvent event; +Display *dpy; +XFontStruct* font; +Conf conf; +int screen; +Window root; +Window bar; +fd_set fd; +Atom wm_atom[WMLast]; +Atom net_atom[NetLast]; +Cursor cursor[CurLast]; +int mw, mh; +int fonth; +int barheight; +Client *clients; /* Fisrt Client */ +Client *sel; /* selected client */ +int seltag; /* selected tag */ +#endif /* LOCAL_H */ diff --git a/wmfs.c b/wmfs.c new file mode 100644 index 0000000..9f7c837 --- /dev/null +++ b/wmfs.c @@ -0,0 +1,947 @@ +/* Copyright (c) 1998, Regents of the University of California +* All rights reserved. +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the University of California, Berkeley nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "local.h" + +unsigned int numlockmask = 0; + +int taglen[MAXTAG] = {3}; + +void +attach(Client *c) { + if(clients) + clients->prev = c; + c->next = clients; + clients = c; + return; +} + +int +clientpertag(int tag) { + Client *c; + int i = 0; + for(c = clients; c; c = c->next) { + if(c->tag == tag) + ++i; + } + return i; +} + +void +detach(Client *c) { + if(c->prev) c->prev->next = c->next; + if(c->next) c->next->prev = c->prev; + if(c == clients) clients = c->next; + c->next = c->prev = NULL; + return; +} + +void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + if(!res) + fprintf(stderr,"fatal: could not malloc() %u bytes\n", size); + return res; +} + +int +errorhandler(Display *d, XErrorEvent *event) { + char mess[512]; + XGetErrorText(d, event->error_code, mess, 128); + fprintf(stderr, "WMFS error: %s(%d) opcodes %d/%d\n resource 0x%lx\n", mess, + event->error_code, + event->request_code, + event->minor_code, + event->resourceid); + return(1); +} + +void +focus(Client *c) { + if(sel && sel != c) { + grabbuttons(sel, False); + setborder(sel->win, conf.bordernormal); + setborder(sel->tbar, conf.bordernormal); + } + + if(c) grabbuttons(c, True); + + sel = c; + + if(c) { + setborder(c->win, conf.borderfocus); + setborder(sel->tbar, conf.borderfocus); + if(conf.raisefocus) + raiseclient(c); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + updatetitle(c); + } + return; +} + +Client* +getbutton(Window w) { + Client *c; + for(c = clients; c && c->button != w; c = c->next); + return c; +} + +Client* +getclient(Window w) { + Client *c; + for(c = clients; c && c->win != w; c = c->next); + return c; +} + +Client* +getnext(Client *c) { + for(; c; c = c->prev); + return c; +} + +Client* +gettbar(Window w) { + Client *c; + for(c = clients; c && c->tbar != w; c = c->next); + return c; +} + +void +getevent(void) { + XEvent event; + XWindowAttributes at; + Client *c; + int i; + struct timeval tv; + if(QLength(dpy) > 0) { + XNextEvent(dpy, &event); + } else { + XFlush(dpy); + FD_ZERO(&fd); + FD_SET(ConnectionNumber(dpy), &fd); + event.type = LASTEvent; + tv.tv_sec = 1; + tv.tv_usec = 0; + if(select(FD_SETSIZE, &fd, NULL, NULL, &tv) > 0) { + XNextEvent(dpy, &event); + } + } + + switch (event.type) { + case EnterNotify: + if(event.xcrossing.mode != NotifyNormal + || event.xcrossing.detail == NotifyInferior) return; + if((c = getclient(event.xcrossing.window)) + || (c = gettbar(event.xcrossing.window))) + focus(c); + break; + + case MapRequest: + if(!XGetWindowAttributes(dpy, event.xmaprequest.window, &at)) return; + if(at.override_redirect) return; + if(!getclient(event.xmaprequest.window)) + manage(event.xmaprequest.window, &at); + break; + + case MappingNotify: + if(event.xmapping.request == MappingKeyboard) + grabkeys(); + break; + + case PropertyNotify: + if(event.xproperty.state == PropertyDelete) + return; + if(event.xproperty.atom == XA_WM_NAME && + event.xproperty.state == PropertyNewValue) { + if((c = getclient(event.xproperty.window))) { + if(c->title) { + XFree(c->title); + c->title = NULL; + updatetitle(c); + } + } + } + break; + + case UnmapNotify: + if((c = getclient(event.xunmap.window))) { + unmanage(c); + } + break; + + case DestroyNotify: + if((c = getclient(event.xdestroywindow.window))) { + unmanage(c); + } + break; + + case FocusIn: + if(sel && event.xfocus.window != sel->win) + XSetInputFocus(dpy, sel->win, RevertToPointerRoot, CurrentTime); + break; + + case KeyPress: keypress(&event); break; + + case ButtonPress: + /* Window and Tbar */ + if((c = gettbar(event.xbutton.window)) + || (c = getclient(event.xbutton.window))) { + raiseclient(c); + if(event.xbutton.button == Button1) + mouseaction(c, event.xbutton.x_root, event.xbutton.y_root, Move); /* type 0 for move */ + else if(event.xbutton.button == Button2) + tile(NULL); + else if(event.xbutton.button == Button3) + mouseaction(c, event.xbutton.x_root, event.xbutton.y_root, Resize); /* type 1 for resize */ + /* Button */ + } else if((c = getbutton(event.xbutton.window))) { + if(event.xbutton.button == Button1) { + unmanage(c); + XKillClient(dpy, c->win); + } else if(event.xbutton.button == Button3) + togglemax(NULL); + /* Bar */ + } + else if(event.xbutton.window == bar) { + for(i = 0; i < conf.ntag + 1; ++i) { + if(event.xbutton.x > taglen[i-1] + && event.xbutton.x < taglen[i]) { + if(event.xbutton.button == Button1) { + tagn(i); + } + } + } + } + else if(event.xbutton.window == root) { + if(event.xbutton.button == Button4) + tagswitch("+1"); + else if(event.xbutton.button == Button5) + tagswitch("-1"); + } + break; + } + return; +} + +void +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + XUngrabButton(dpy, AnyButton, AnyModifier, c->tbar); + XUngrabButton(dpy, AnyButton, AnyModifier, c->button); + + if(focused) { + /* Window */ + XGrabButton(dpy, Button1, ALT, c->win, 0, ButtonMask,GrabModeAsync,GrabModeSync, None, None); + XGrabButton(dpy, Button1, ALT|LockMask, c->win, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, ALT, c->win, 0, ButtonMask,GrabModeAsync,GrabModeSync, None, None); + XGrabButton(dpy, Button2, ALT|LockMask, c->win, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, ALT, c->win, 0, ButtonMask,GrabModeAsync,GrabModeSync, None, None); + XGrabButton(dpy, Button3, ALT|LockMask, c->win, False, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + /* Titlebar */ + XGrabButton(dpy, Button1, AnyModifier, c->tbar, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, AnyModifier, c->tbar, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, AnyModifier, c->tbar, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + /* Button */ + XGrabButton(dpy, Button1, AnyModifier, c->button, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, AnyModifier, c->button, 0, ButtonMask,GrabModeAsync, GrabModeSync, None, None); + } else { + XGrabButton(dpy, AnyButton, AnyModifier, c->win, 0, ButtonMask, GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, AnyButton, AnyModifier, c->tbar, 0, ButtonMask, GrabModeAsync, GrabModeSync, None, None); + + + XGrabButton(dpy, AnyButton, AnyModifier, c->button, 0, ButtonMask, GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) { + unsigned int i; + KeyCode code; + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for(i = 0; i < LEN(keys); i++) { + code = XKeysymToKeycode(dpy, keys[i].keysym); + XGrabKey(dpy, code, keys[i].mod, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, keys[i].mod|numlockmask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, keys[i].mod|LockMask, root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, keys[i].mod|LockMask|numlockmask, root, True, GrabModeAsync, GrabModeAsync); + } + return; +} + +void +hide(Client *c) { + if(c) { + XMoveWindow(dpy,c->win,c->x,c->y+mh*2); + XMoveWindow(dpy,c->tbar,c->x,c->y+mh*2); + XMoveWindow(dpy,c->button,c->x,c->y+mh*2); + } +} + + + + +void +init(void) { + XSetWindowAttributes at; + XModifierKeymap *modmap; + int i, j; + + /* FIRST INIT */ + gc = DefaultGC (dpy, screen); + screen = DefaultScreen (dpy); + root = RootWindow (dpy, screen); + mw = DisplayWidth (dpy, screen); + mh = DisplayHeight (dpy, screen); + seltag = 1; + init_conf(); + + /* INIT FONT */ + font = XLoadQueryFont(dpy, conf.font); + if(!font){ + fprintf(stderr, "XLoadQueryFont: failed loading font '%s'\n", conf.font); + exit(0); + + } + XSetFont(dpy, gc, font->fid); + fonth = font->ascent + font->descent; + barheight = fonth + 3; + + /* INIT CURSOR */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + + /* INIT MODIFIER */ + modmap = XGetModifierMapping(dpy); + for(i = 0; i < 8; i++) + for(j = 0; j < modmap->max_keypermod; j++) { + if(modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + } + XFreeModifiermap(modmap); + + /* INIT ATOM */ + wm_atom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wm_atom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wm_atom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + net_atom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + net_atom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, net_atom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) net_atom, NetLast); + + /* INIT ROOT */ + at.event_mask = KeyMask | ButtonPressMask | ButtonReleaseMask | + SubstructureRedirectMask | SubstructureNotifyMask | + EnterWindowMask | LeaveWindowMask | StructureNotifyMask ; + at.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &at); + + /* INIT BAR */ + at.override_redirect = 1; + at.background_pixmap = ParentRelative; + at.event_mask = ButtonPressMask | ExposureMask; + bar = XCreateWindow(dpy, root, 0, 0, mw, barheight, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &at); + XSetWindowBackground(dpy, bar, conf.barcolor); + XMapWindow(dpy, bar); + + /* INIT STUFF */ + XSetErrorHandler(errorhandler); + grabkeys(); + + return; +} + +Bool +ishide(Client *c) { + int i; + for(i = 0; i < conf.ntag+1; ++i) + if(c->tag == i && seltag == i) + return False; + return True; +} + +void +keymovex(char *cmd) { + if(sel) { + if(cmd && !ishide(sel)) { + int tmp; + tmp = sel->x + atoi(cmd); + moveresize(sel,tmp, sel->y, sel->w, sel->h); + } + } + return; +} + +void +keymovey(char *cmd) { + if(sel && !ishide(sel)) { + if(cmd) { + int tmp; + tmp = sel->y + atoi(cmd); + moveresize(sel, sel->x, tmp, sel->w, sel->h); + } + } + return; +} + +void +keypress(XEvent *e) { + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < LEN(keys); i++) + if(keysym == keys[i].keysym + && (keys[i].mod & ~(numlockmask | LockMask)) == + (ev->state & ~(numlockmask | LockMask)) + && keys[i].func) + { + keys[i].func(keys[i].cmd); + } + return; +} + +void +keyresize(char *cmd) { + if(sel && !ishide(sel)) { + int temph=0, tempw=0, modh=0, modw=0, + tmp=0; + + switch(cmd[1]) { + case 'h': tmp = (cmd[0] == '+') ? 5 : -5; modh = tmp; break; + case 'w': tmp = (cmd[0] == '+') ? 5 : -5; modw = tmp; break; + } + + temph = sel->h + modh; + tempw = sel->w + modw; + temph = (temph < 10) ? 10 : temph; + tempw = (tempw < 10) ? 10 : tempw; + moveresize(sel, sel->x, sel->y, tempw, temph); + } + return; +} + +void +killclient(char *cmd) { + if(sel && !ishide(sel)) { + XEvent ev; + ev.type = ClientMessage; + ev.xclient.window = sel->win; + ev.xclient.message_type = wm_atom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = wm_atom[WMDelete]; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, sel->win, False, NoEventMask, &ev); + } + return; +} + +void +mapclient(Client *c) { + if(c) { + XMapWindow(dpy, c->win); + XMapWindow(dpy, c->tbar); + XMapWindow(dpy, c->button); + } + return; +} + +void +manage(Window w, XWindowAttributes *wa) { + Client *c, *t = NULL; + Window trans; + Status rettrans; + + c = emallocz(sizeof(Client)); + c->win = w; + c->x = wa->x; + c->y = wa->y + conf.ttbarheight + barheight; + c->w = wa->width; + c->h = wa->height; + c->border = wa->border_width; + c->tag = seltag; + c->layout = Free; + + setborder(w, conf.bordernormal); + + XSelectInput(dpy, w, EnterWindowMask | FocusChangeMask | + PropertyChangeMask | StructureNotifyMask); + if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) + for(t = clients; t && t->win != trans; t = t->next); + + c->tbar = XCreateSimpleWindow(dpy,root, + c->x, + c->y - conf.ttbarheight, + c->w, + conf.ttbarheight, + conf.borderheight, + conf.bordernormal, + conf.barcolor); + XSelectInput(dpy, c->tbar, ExposureMask | EnterWindowMask); + + c->button = XCreateSimpleWindow(dpy,root, + c->x + c->w - 10, + BUTY(c->y), + 5, + BUTH, + 1, + conf.buttoncolor, + conf.buttoncolor); + XSelectInput(dpy, c->button, ExposureMask | EnterWindowMask); + + grabbuttons(c, False); + attach(c); + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + mapclient(c); + updatetitle(c); + setborder(c->tbar, conf.bordernormal); + focus(c); + return; +} + +/* If the type is 0, this function will move, else, + this will resize */ + +void +mouseaction(Client *c, int x, int y, int type) { + int ocx, ocy; + XEvent ev; + + ocx = c->x; + ocy = c->y; + + if(XGrabPointer(dpy, root, 0, MouseMask, GrabModeAsync, GrabModeAsync, + None, cursor[((type) ?CurResize:CurMove)], CurrentTime) != GrabSuccess) return; + c->max = False; + + if(type) + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w, c->h); + + for(;;) { + XMaskEvent(dpy, MouseMask | ExposureMask | SubstructureRedirectMask, &ev); + if(ev.type == ButtonRelease) { + if(type) + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w, c->h); + XUngrabPointer(dpy, CurrentTime); + return; + } else if(ev.type == MotionNotify) { + + if(c->y <= barheight + conf.ttbarheight) updatebar(); + XSync(dpy, 0); + + if(type) /* Resize */ + moveresize(c, c->x, c->y, + ((ev.xmotion.x - ocx <= 0) ? 1 : ev.xmotion.x - ocx), + ((ev.xmotion.y - ocy <= 0) ? 1 : ev.xmotion.y - ocy)); + else /* Move */ + moveresize(c, + (ocx + (ev.xmotion.x - x)), + (ocy + (ev.xmotion.y - y)), + c->w, c->h); + } + } + return; +} + +void +moveresize(Client *c, int x, int y, int w, int h) { + if(c) { + if(w <= 0 || h <= 0) + return; + c->layout = Free; + c->max = False; + if(c->x != x || c->y != y || c->w != w || c->h != h) { + c->x = x; + c->y = y; + c->w = w; + c->h = h; + XMoveResizeWindow(dpy, c->win, x, y, w ,h); + XMoveResizeWindow(dpy, c->tbar, x, y - conf.ttbarheight, w, conf.ttbarheight); + XMoveResizeWindow(dpy, c->button, + (x + w - 10), + BUTY(y), + 5, + BUTH); + updateall(); + XSync(dpy, False); + } + } + return; +} + +void +raiseclient(Client *c) { + if(c) { + XRaiseWindow(dpy,c->win); + XRaiseWindow(dpy,c->tbar); + XRaiseWindow(dpy,c->button); + } + return; +} + +void +scan(void) { + unsigned int i, num; + Window *wins, d1, d2; + XWindowAttributes wa; + + wins = NULL; + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); + return; +} + +void +setborder(Window win, int color) { + XSetWindowBorder(dpy, win, color); + XSetWindowBorderWidth(dpy, win, conf.borderheight); + return; +} + +void +spawn(char *cmd) { + if(strlen(cmd) > 0 && !fork()) { + execl(getenv("SHELL"), "sh", "-c", cmd, NULL); + exit(1); + } + return; +} + +void +tag(char *cmd) { + Client *c; + int tmp = atoi(cmd); + + if(tmp > conf.ntag || tmp < 1 || tmp == seltag) + return; + for(c = clients; c; c = c->next) { + if(!ishide(c)) + hide(c); + if(c->tag == tmp) { + unhide(c); + updateall(); + } + } + seltag = tmp; + sel = NULL; + return; +} + +void +tagn(int tag) { + Client *c; + if(tag > conf.ntag || tag < 1 || tag == seltag) + return; + for(c = clients; c; c = c->next) { + if(!ishide(c)) + hide(c); + if(c->tag == tag) { + unhide(c); + updateall(); + } + } + seltag = tag; + sel = NULL; + return; +} + +void +tagswitch(char *cmd) { + Client *c; + int tmp; + + tmp = atoi(cmd); + + if(seltag + tmp > conf.ntag || seltag + tmp < 1) + return; + + seltag += tmp; + for(c = clients; c; c = c->next) { + if(c->tag == seltag - tmp) + hide(c); + + if(c->tag == seltag) { + unhide(c); + updateall(); + } + } + sel = NULL; + return; +} + +void +tile(char *cmd) { + if(sel) { + Client *c; + int i; + unsigned int x, y, w, h, bord; + unsigned int barto; + + barto = conf.ttbarheight + barheight; + bord = conf.borderheight * 2; + + x = mw / 2 + conf.borderheight; + y = barto; + w = ((mw - bord ) / 2 - bord); + if(clientpertag(seltag)-1) + h = ((mh-bord) - conf.ttbarheight - barheight) / (clientpertag(seltag) - 1) ; + + /* Master client in first (always the sel window) */ + moveresize(sel, 0, barto, + ((clientpertag(seltag) > 1) ? (mw-bord) / 2 : (mw-bord)), + ((mh-bord) - conf.ttbarheight - barheight)); + sel->layout = Tile; + + /* tiling */ + for(i=0, c = clients; c; c = c->next, ++i) { + if(c != sel && !ishide(c)) { + moveresize(c, x, y, w, h); + if(i < i + 1) + y = c->y + c->h + bord + conf.ttbarheight; + c->layout = Tile; + } + } + } + return; +} + +void +togglemax(char *cmd) { + if(sel && !ishide(sel)) { + if(!sel->max) { + sel->ox = sel->x; + sel->oy = sel->y; + sel->ow = sel->w; + sel->oh = sel->h; + moveresize(sel, 0, + conf.ttbarheight + barheight, + (mw-(conf.borderheight * 2)), + (mh-(conf.borderheight * 2)- conf.ttbarheight - barheight)); + sel->max = True; + sel->layout = Max; + } else if(sel->max) { + moveresize(sel, sel->ox, sel->oy, sel->ow, sel->oh); + sel->max = False; + sel->layout = Free; + } + } + return; +} + +void +unhide(Client *c) { + if(c) { + XMoveWindow(dpy,c->win,c->x,c->y); + XMoveWindow(dpy,c->tbar,c->x, + (c->y - conf.ttbarheight)); + XMoveWindow(dpy,c->button, + (c->x + c->w -10), + (c->y - 9)); + } +} + +void +unmanage(Client *c) { + XSetErrorHandler(errorhandler); + if(sel == c) + sel = c->next; + else + sel = NULL; + XUnmapWindow(dpy, c->tbar); + XDestroyWindow(dpy, c->tbar); + XUnmapWindow(dpy, c->button); + XDestroyWindow(dpy, c->button); + detach(c); + free(c); + XSync(dpy, False); + return; +} + +void +updateall(void) { + Client *c; + for(c = clients; c; c = c->next) { + if(!ishide(c)) + updatetitle(c); + } +} + +void +updatebar(void) { + struct tm *tm; + time_t lt; + int slen = 0, i; + char buf[conf.ntag][100] ; + + tm = localtime(<); + lt = time(NULL); + + XClearWindow(dpy, bar); + XSetForeground(dpy, gc, conf.textcolor); + + for(i=0;i< conf.ntag;++i) { + /* Make the tag string */ + if(clientpertag(i+1)) + sprintf(buf[i], "%s(%d) ", conf.taglist[i], clientpertag(i+1)); + else + sprintf(buf[i], "%s() ", conf.taglist[i]); + taglen[i+1] = taglen[i] + 6*(strlen(conf.taglist[i]) + ((clientpertag(i+1) >= 10) ? 5 : 4)); + /* Rectangle for the tag background */ + if(i+1 == seltag) XSetForeground(dpy, gc, conf.tagselbg); + else XSetForeground(dpy, gc, 0x090909); + XFillRectangle(dpy, bar, gc, taglen[i]-4, 0, strlen(buf[i])*6, barheight); + /* Draw tag */ + if(i+1 == seltag) XSetForeground(dpy, gc, conf.tagselfg); + else XSetForeground(dpy, gc, conf.textcolor); + XDrawString(dpy, bar, gc, taglen[i], fonth-1, buf[i], strlen(buf[i])); + } + + /* Draw layout symbol */ + XSetForeground(dpy, gc, conf.tagselfg); + XDrawString(dpy, bar, gc, taglen[conf.ntag], + fonth-1, + (sel) ? conf.symlayout[sel->layout] : conf.symlayout[Free], + (sel) ? strlen(conf.symlayout[sel->layout]) :strlen(conf.symlayout[Free]) ); + + XSetForeground(dpy, gc, conf.textcolor); + XDrawLine(dpy, bar, gc, mw-slen*6-5, 0 , mw-slen*6-5 , barheight); + return; +} + +void +unmapclient(Client *c) { + if(c) { + XUnmapWindow(dpy, c->win); + XUnmapWindow(dpy, c->tbar); + XUnmapWindow(dpy, c->button); + } + return; +} + +void +updatetitle(Client *c) { + XFetchName(dpy, c->win, &(c->title)); + if(!c->title) + c->title = strdup("WMFS"); + XClearWindow(dpy, c->tbar); + XSetForeground(dpy, gc, conf.textcolor); + XDrawString(dpy, c->tbar, gc, 5, 10, c->title, strlen(c->title)); + return; +} + +void +wswitch(char *cmd) { + if(sel && !ishide(sel)) { + Client *c; + if(cmd[0] == '+') { + for(c = sel->next; c && ishide(c); c = c->next); + if(!c) + for(c = clients; c && ishide(c); c = c->next); + if(c) { + focus(c); + raiseclient(c); + } + } else if(cmd[0] == '-') { + for(c = sel->prev; c && ishide(c); c = c->prev); + if(!c) { + for(c = clients; c && c->next; c = c->next); + for(; c && ishide(c); c = c->prev); + } + if(c) { + focus(c); + raiseclient(c); + } + } + } + return; +} + +int +main(int argc,char **argv) { + dpy = XOpenDisplay(NULL); + int i; + + static struct option long_options[] ={ + {"help", 0, NULL, 'h'}, + {"info", 0, NULL, 'i'}, + {"version", 0, NULL, 'v'}, + {NULL, 0, NULL, 0} + }; + + while ((i = getopt_long (argc, argv, "hvi", long_options, NULL)) != -1) { + switch (i) { + case 'h': + default: + printf("Usage: wmfs [OPTION]\n" + " -h, --help show this page\n" + " -i, --info show informations\n" + " -v, --version show WMFS version\n"); + exit(EXIT_SUCCESS); + break; + case 'i': + printf("WMFS - Window Manager From Scratch. By :\n" + " - Martun Duquesnoy (code)\n" + " - Marc Lagrange (build system)\n"); + exit(EXIT_SUCCESS); + break; + case 'v': + printf("WMFS version : "WMFS_VERSION".\n" + " Compilation settings :\n" + " - Flags : "WMFS_COMPILE_FLAGS"\n" + " - Linked Libs : "WMFS_LINKED_LIBS"\n" + " - On "WMFS_COMPILE_MACHINE" by "WMFS_COMPILE_BY".\n"); + exit(EXIT_SUCCESS); + break; + } + } + + if(!dpy) {printf("wmfs: cannot open X server\n"); exit(0);} + + init(); + scan(); + + for(;;) { + getevent(); + updatebar(); + } + + XCloseDisplay(dpy); + return 0; +} diff --git a/wmfsrc b/wmfsrc new file mode 100644 index 0000000..47c9336 --- /dev/null +++ b/wmfsrc @@ -0,0 +1,72 @@ +# WMFS config file + +misc +{ + font = "*-fixed-medium-*-12-*" + raisefocus = false + raiseswitch = true + border_height = 1 + titlebar_height = 12 +} + +colors +{ + border_normal = 0x354B5C + border_focus = 0x6286A1 + bar = 0x090909 + button = 0x354B5C + text = 0x6289A1 + tag_sel_fg = 0xFFFFFF + tag_sel_bg = 0x354B5C +} + +layout +{ + layout_symbol = { "[Free]", "[Tile]", "[Max]" } +} + +tag +{ + tag = { "1", "2", "3", "4", "5", "6", "7", "8", "9" } +} + +keys +{ +#general keybind + + key { mod = {"Control"} key = "Return" func = "spawn" cmd = "urxvt" } + key { mod = {"Alt"} key = "t" func = "spawn" cmd = "thunar" } + key { mod = {"Alt"} key = "q" func = "killclient" cmd = NULL } + key { mod = {"Control"} key = "t" func = "togglemax" cmd = NULL } + key { mod = {"Control"} key = "o" func = "tile" cmd = NULL } + key { mod = {"Alt"} key = "Tab" func = "wswitch" cmd = "+" } + key { mod = {"Alt","Shift"} key = "Tab" func = "wswitch" cmd = "-" } + key { mod = {"Control"} key = "Right" func = "tagswitch" cmd = "+1" } + key { mod = {"Control"} key = "Left" func = "tagswitch" cmd = "-1" } + +# moving client keybind + + key { mod = {"Control","Alt"} key = "Left" func = "keymovex" cmd = "-10" } + key { mod = {"Control","Alt"} key = "Right" func = "keymovex" cmd = "+10" } + key { mod = {"Control","Alt"} key = "Up" func = "keymovey" cmd = "-10" } + key { mod = {"Control","Alt"} key = "Down" func = "keymovey" cmd = "+10" } + +# resize client keybind + + key { mod = {"Shift","Alt"} key = "Down" func = "keyresize" cmd = "+h" } + key { mod = {"Shift","Alt"} key = "Right" func = "keyresize" cmd = "+w" } + key { mod = {"Shift","Alt"} key = "Up" func = "keyresize" cmd = "-h" } + key { mod = {"Shift","Alt"} key = "Left" func = "keyresize" cmd = "-w" } + +# tag switching keybind + + key { mod = {"Alt"} key = "F1" func = "tag" cmd = "1" } + key { mod = {"Alt"} key = "F2" func = "tag" cmd = "2" } + key { mod = {"Alt"} key = "F3" func = "tag" cmd = "3" } + key { mod = {"Alt"} key = "F4" func = "tag" cmd = "4" } + key { mod = {"Alt"} key = "F5" func = "tag" cmd = "5" } + key { mod = {"Alt"} key = "F6" func = "tag" cmd = "6" } + key { mod = {"Alt"} key = "F7" func = "tag" cmd = "7" } + key { mod = {"Alt"} key = "F8" func = "tag" cmd = "8" } + key { mod = {"Alt"} key = "F9" func = "tag" cmd = "9" } +}