Initial commit - New improvments :

· Configuration with ~/.wmfsrc with libconfig
        · Bugfixes
        · Layouts and tile
        · "virtuals desktops"
        · Bugs added :D
Signed-off-by: Marc Lagrange <markocpc@gmail.com>
This commit is contained in:
Marc Lagrange 2008-08-10 17:37:24 +02:00
commit 9e98027673
8 changed files with 1326 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
Makefile
*.o
.*.sw?
wmfs
config.h
CHANGELOG
config.h
#*
\#*

80
CMakeLists.txt Normal file
View File

@ -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()

28
COPYING Executable file
View File

@ -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.

10
README Executable file
View File

@ -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 <xorg62@gmail.com>
* Build System & other : Marc Lagrange <markocpc@gmail.com>
License: BSD

14
config.h.in Normal file
View File

@ -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 */

163
local.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef LOCAL_H
#define LOCAL_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <getopt.h>
#include <confuse.h>
#include <sys/time.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <X11/Xutil.h>
#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 */

947
wmfs.c Normal file
View File

@ -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);
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;
}

72
wmfsrc Normal file
View File

@ -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" }
}