From 9e980276737f50bcb880077e536c7d5a79a0b32b Mon Sep 17 00:00:00 2001 From: Marc Lagrange Date: Sun, 10 Aug 2008 17:37:24 +0200 Subject: [PATCH] =?UTF-8?q?Initial=20commit=20-=20New=20improvments=20:=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=C2=B7=20Configuration=20with=20~/.wmfs?= =?UTF-8?q?rc=20with=20libconfig=20=20=20=20=20=20=20=20=20=C2=B7=20Bugfix?= =?UTF-8?q?es=20=20=20=20=20=20=20=20=20=C2=B7=20Layouts=20and=20tile=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=C2=B7=20"virtuals=20desktops"=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=C2=B7=20Bugs=20added=20:D=20Signed-off-by:?= =?UTF-8?q?=20Marc=20Lagrange=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 12 + CMakeLists.txt | 80 +++++ COPYING | 28 ++ README | 10 + config.h.in | 14 + local.h | 163 +++++++++ wmfs.c | 947 +++++++++++++++++++++++++++++++++++++++++++++++++ wmfsrc | 72 ++++ 8 files changed, 1326 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100755 COPYING create mode 100755 README create mode 100644 config.h.in create mode 100644 local.h create mode 100644 wmfs.c create mode 100644 wmfsrc 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" } +}