From 4eca4bdcfadb83557da5ecf2e609969f32c791ba Mon Sep 17 00:00:00 2001 From: Martin Duquesnoy Date: Fri, 23 Sep 2011 23:01:17 +0200 Subject: [PATCH] Branch for wmfs2 --- .gitignore | 2 + src/barwin.c | 124 +++++++++ src/barwin.h | 32 +++ src/barwin.o | Bin 0 -> 4424 bytes src/client.c | 432 ++++++++++++++++++++++++++++++ src/client.h | 130 +++++++++ src/client.o | Bin 0 -> 19776 bytes src/config.c | 216 +++++++++++++++ src/config.h | 115 ++++++++ src/config.o | Bin 0 -> 15696 bytes src/draw.h | 42 +++ src/event.c | 260 ++++++++++++++++++ src/event.h | 20 ++ src/event.o | Bin 0 -> 7032 bytes src/ewmh.c | 106 ++++++++ src/ewmh.h | 74 ++++++ src/ewmh.o | Bin 0 -> 11400 bytes src/infobar.c | 246 +++++++++++++++++ src/infobar.h | 74 ++++++ src/infobar.o | Bin 0 -> 8640 bytes src/layout.c | 316 ++++++++++++++++++++++ src/layout.h | 40 +++ src/layout.o | Bin 0 -> 7008 bytes src/parse.c | 693 ++++++++++++++++++++++++++++++++++++++++++++++++ src/parse.h | 118 +++++++++ src/parse.o | Bin 0 -> 13480 bytes src/parse_api.c | 214 +++++++++++++++ src/parse_api.o | Bin 0 -> 5312 bytes src/screen.c | 114 ++++++++ src/screen.h | 27 ++ src/screen.o | Bin 0 -> 3608 bytes src/tag.c | 198 ++++++++++++++ src/tag.h | 38 +++ src/tag.o | Bin 0 -> 6504 bytes src/util.c | 128 +++++++++ src/util.h | 63 +++++ src/util.o | Bin 0 -> 4712 bytes src/wmfs.c | 344 ++++++++++++++++++++++++ src/wmfs.h | 210 +++++++++++++++ src/wmfs.o | Bin 0 -> 9784 bytes wmfsrc2 | 149 +++++++++++ 41 files changed, 4525 insertions(+) create mode 100644 .gitignore create mode 100644 src/barwin.c create mode 100644 src/barwin.h create mode 100644 src/barwin.o create mode 100644 src/client.c create mode 100644 src/client.h create mode 100644 src/client.o create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/config.o create mode 100644 src/draw.h create mode 100644 src/event.c create mode 100644 src/event.h create mode 100644 src/event.o create mode 100644 src/ewmh.c create mode 100644 src/ewmh.h create mode 100644 src/ewmh.o create mode 100644 src/infobar.c create mode 100644 src/infobar.h create mode 100644 src/infobar.o create mode 100644 src/layout.c create mode 100644 src/layout.h create mode 100644 src/layout.o create mode 100644 src/parse.c create mode 100644 src/parse.h create mode 100644 src/parse.o create mode 100644 src/parse_api.c create mode 100644 src/parse_api.o create mode 100644 src/screen.c create mode 100644 src/screen.h create mode 100644 src/screen.o create mode 100644 src/tag.c create mode 100644 src/tag.h create mode 100644 src/tag.o create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 src/util.o create mode 100644 src/wmfs.c create mode 100644 src/wmfs.h create mode 100644 src/wmfs.o create mode 100644 wmfsrc2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7280bef --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Makefile +wmfs diff --git a/src/barwin.c b/src/barwin.c new file mode 100644 index 0000000..9c473c1 --- /dev/null +++ b/src/barwin.c @@ -0,0 +1,124 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include "wmfs.h" +#include "barwin.h" +#include "util.h" + +/** Create a barwin + * \param parent Parent window of the BarWindow + * \param x X position + * \param y Y position + * \param w barwin Width + * \param h barwin Height + * \param color barwin color + * \param entermask bool for know if the EnterMask mask is needed + * \return The BarWindow pointer +*/ +struct barwin* +barwin_new(Window parent, int x, int y, int w, int h, Color fg, Color bg, bool entermask) +{ + struct barwin *b = (struct barwin*)xcalloc(1, sizeof(struct barwin)); + XSetWindowAttributes at = + { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = BARWIN_MASK + }; + + if(entermask) + at.event_mask |= BARWIN_ENTERMASK; + + /* Create window */ + b->win = XCreateWindow(W->dpy, parent, + x, y, w, h, + 0, W->xdepth, + CopyFromParent, + DefaultVisual(W->dpy, W->xscreen), + BARWIN_WINCW, + &at); + + b->dr = XCreatePixmap(W->dpy, parent, w, h, W->xdepth); + + /* Property */ + b->geo.x = x; + b->geo.y = y; + b->geo.w = w; + b->geo.h = h; + b->bg = bg; + b->fg = fg; + + SLIST_INIT(&b->mousebinds); + + /* Attach */ + SLIST_INSERT_HEAD(&W->h.barwin, b, next); + + return b; +} + +/** Delete a barwin + * \param bw barwin pointer +*/ +void +barwin_remove(struct barwin *b) +{ + SLIST_REMOVE(&W->h.barwin, b, barwin, next); + + XSelectInput(W->dpy, b->win, NoEventMask); + XDestroyWindow(W->dpy, b->win); + XFreePixmap(W->dpy, b->dr); + + /* Free mousebinds */ + FREE_LIST(mousebind, b->mousebinds); + + free(b); +} + +/** Resize a barwin + * \param bw barwin pointer + * \param w Width + * \param h Height +*/ +void +barwin_resize(struct barwin *b, int w, int h) +{ + /* Frame */ + XFreePixmap(W->dpy, b->dr); + + b->dr = XCreatePixmap(W->dpy, W->root, w, h, W->xdepth); + + b->geo.w = w; + b->geo.h = h; + + XResizeWindow(W->dpy, b->win, w, h); +} + +void +barwin_mousebind_new(struct barwin *b, unsigned int button, bool u, struct geo a, void (*func)(Uicb), Uicb cmd) +{ + struct mousebind *m = (struct mousebind*)xcalloc(1, sizeof(struct mousebind)); + + m->button = button; + m->use_area = u; + m->area = a; + m->func = func; + + m->cmd = (cmd ? xstrdup(cmd) : NULL); + + SLIST_INSERT_HEAD(&b->mousebinds, m, next); +} + +/** Refresh the barwin Color + * \param bw barwin pointer +*/ +void +barwin_refresh_color(struct barwin *b) +{ + XSetForeground(W->dpy, W->gc, b->bg); + XFillRectangle(W->dpy, b->dr, W->gc, 0, 0, b->geo.w, b->geo.h); +} + + + diff --git a/src/barwin.h b/src/barwin.h new file mode 100644 index 0000000..cc9b693 --- /dev/null +++ b/src/barwin.h @@ -0,0 +1,32 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef BARWIN_H +#define BARWIN_H + +#include "wmfs.h" + +#define BARWIN_MASK \ + (SubstructureRedirectMask | SubstructureNotifyMask \ + | ButtonMask | MouseMask | ExposureMask | VisibilityChangeMask \ + | StructureNotifyMask | SubstructureRedirectMask) + +#define BARWIN_ENTERMASK (EnterWindowMask | LeaveWindowMask | FocusChangeMask) +#define BARWIN_WINCW (CWOverrideRedirect | CWBackPixmap | CWEventMask) + +#define barwin_delete_subwin(b) XDestroySubwindows(W->dpy, b->win) +#define barwin_map_subwin(b) XMapSubwindows(W->dpy, b->win) +#define barwin_unmap_subwin(b) XUnmapSubwindows(W->dpy, b->win) +#define barwin_refresh(b) XCopyArea(W->dpy, b->dr, b->win, W->gc, 0, 0, b->geo.w, b->geo.h, 0, 0) +#define barwin_map(b) XMapWindow(W->dpy, b->win); +#define barwin_unmap(b) XUnmapWindow(W->dpy, b->win); + +struct barwin* barwin_new(Window parent, int x, int y, int w, int h, Color fg, Color bg, bool entermask); +void barwin_remove(struct barwin *b); +void barwin_resize(struct barwin *b, int w, int h); +void barwin_mousebind_new(struct barwin *b, unsigned int button, bool u, struct geo a, void (*func)(Uicb), Uicb cmd); +void barwin_refresh_color(struct barwin *b); + +#endif /* BARWIN_H */ diff --git a/src/barwin.o b/src/barwin.o new file mode 100644 index 0000000000000000000000000000000000000000..27776af31dae1f5d2255f8e1c1d28482e2b26d9e GIT binary patch literal 4424 zcmbVQU1(HC6rMG!#v0w##x_O4m8PIj(+IX`X?2(0n@-AJ8)H{f?KWb~0#vs1j%`zEndCZJ;l)4>qEZluFHmpwv|IklF+ST_q3cAL%)BXUyfg_r-y` zbIv#CZ_b>1<|ZHO;hH5LPYIF3Lv|65Vr+AM^&%4#WRR>R%SeQ>-$VQN(NnVpK>PIy zjYs^Hot$RbDHfSz#)QW7yBafYYb-Ke>B;N2enk6MDf~>MA0!U9Z(t*H(sI@*U5TA-u)K=|T%GO7EVZmSVJR=qeJ$+7i;@e-c1b0!Mou=$u5Nz?t6lJG( zAZ1KyT%XXmaW~A%g{aOCmMhw5uJ=>EL7G@k`RBOV+N<4`6Jq$jdsOSWZ}9!WQEw%J z8vCHKbX2SKBNJxtRaQ{o0vJ}93+D)KW_{biyOlYf9iglS{CK6G9T+N)Oh8Zy)OfJm zE?vQmDa!Q`Rth^11od)b!lD2JMOdU|+!(j0N^ab?r~poMG+2!hD*KcN>mZpBNC>>c zIxn+=VIjbQKXn5u<51|s=yl&lQ7^P-wi-%g-eF>%?i};=ZF<6X5bQ46oO#D7!1Mw* zf(Hc+VWoGC5RxDW%=`*TTa*%#1rhQOASuRTG`J54uG25`W}PnOuTvfYJmqKll*>{D z(0Q1SRb1VXRctcjhZp&DR=};|9pOp$(@K*x` zGlzy4h3dde165WeS(YKW`Dq9+5WU`dR`iDh*dcmm8}j2}=nmo2(J=^jl4U2bAKZg? za4gL9@h~$+!Yndu^#@E_m_-MzP?$ahWzFjR{n2~*=w*mvNbApD4s}29X#FQ){==i? zbLEIb{st-EMtN;1g_tSBOhQ>2LvY>|Ko8S5@xb%ZOIkkrm9KZLIE<6S%G1CM_Btxs z0t&(b4S*Uv!bAY*NhmfjD|k3C;dBzrSHrd&pwvT+uy@}^ji`DLSWQM0z3q5pS@WY{ z)R^acE15ngbFY?m;tJ(QA#w%n71A1R5Y{vvf|qp;+H3(D(j4^Pg2u|hl68t$UE(Zo zD}MCRJuzV{0nbVUnsuGGkV~y(h9YoryJnM5@wl=}amUYXa&-hc!z+WyWLD zk{}=3P;Q*FNC|nk;KY$HDSE>qj){MAl)y@{Q9``MV31z=1ilH7HRRDksa#R>dEnPV zJOOD+ymi4+=8e9z_0`Q;c<=^0Vswa zbiwbq;O#DW-UT;Y@G%!W>w>@Ff`8+JlXR;2m8MwgczdF%nOH<)!aPoL&9Qhq*-Q?Q zhPP5?EMp#MPqZYDE3U3R*AeSf3MsQAdCVjY`^>o6oT*K8W;3KgGohN3CzM`I$~2`` zE0Bw->Gm#Atv7L&!ZMDIWHxO!fnO^kFxQf``2;iRw#MdUJefk*nVMwEJd#Rg6D_2n zrad07hgf2XBXJY%;sm&$XK4aYBVg{NKc^AdO3zhslQ+yB-@{)!9!vkQL5X5W6k|G3Dn zk^K_)iyshbUrjdLUgtM$`13aTS7je{?l(*vaKSIw>|noDb^<`- zz}sGyfZ*%I?;@`He9nQZK7Y}HtNUf#iOV=7{L~Y^hWIRVSO`k~Ylk1_9fQ3G_2qF( zEkb4;dTK!)r(y}#4Bn|vtB*B={_QyjmK3D_0y4It?r#O*w;Sp^$D}hMQot#T-(Swy zQB>(4M#eUr`me#7Gj { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include + +#include "client.h" +#include "config.h" +#include "util.h" +#include "barwin.h" +#include "ewmh.h" +#include "layout.h" +#include "draw.h" + +#define CLIENT_MOUSE_MOD Mod1Mask + +#define CLIENT_RESIZE_DIR(D) \ +void uicb_client_resize_##D(Uicb cmd) \ +{ \ + if(W->client) \ + client_fac_resize(W->client, D, ATOI(cmd)); \ +} + +#define CLIENT_ACTION_DIR(A, D) \ +void uicb_client_##A##_##D(Uicb cmd) \ +{ \ + (void)cmd; \ + struct client *c; \ + if(W->client && (c = client_next_with_pos(W->client, D))) \ + client_##A(c); \ +} + +#define CLIENT_ACTION_LIST(A, L) \ +void uicb_client_##A##_##L(Uicb cmd) \ +{ \ + (void)cmd; \ + struct client *c; \ + if(W->client && (c = client_##L(W->client))) \ + client_##A(c); \ +} + +/* uicb_client_resize_dir() */ +CLIENT_RESIZE_DIR(Right) +CLIENT_RESIZE_DIR(Left) +CLIENT_RESIZE_DIR(Top) +CLIENT_RESIZE_DIR(Bottom) + +/* uicb_client_focus_dir() */ +CLIENT_ACTION_DIR(focus, Right) +CLIENT_ACTION_DIR(focus, Left) +CLIENT_ACTION_DIR(focus, Top) +CLIENT_ACTION_DIR(focus, Bottom) + +/* uicb_client_swapsel_dir() */ +#define client_swapsel(c) client_swap(W->client, c) +CLIENT_ACTION_DIR(swapsel, Right) +CLIENT_ACTION_DIR(swapsel, Left) +CLIENT_ACTION_DIR(swapsel, Top) +CLIENT_ACTION_DIR(swapsel, Bottom) + +/* uicb_client_focus_next/prev() */ +CLIENT_ACTION_LIST(focus, next) +CLIENT_ACTION_LIST(focus, prev) + +/* uicb_client_swapsel_next/prev() */ +CLIENT_ACTION_LIST(swapsel, next) +CLIENT_ACTION_LIST(swapsel, prev) + +/** Send a ConfigureRequest event to the struct client + * \param c struct client pointer +*/ +void +client_configure(struct client *c) +{ + XConfigureEvent ev = + { + .type = ConfigureNotify, + .event = c->win, + .window = c->win, + .x = c->geo.x, + .y = c->geo.y, + .width = c->geo.w, + .height = c->geo.h, + .above = None, + .border_width = 0, + .override_redirect = 0 + }; + + XSendEvent(W->dpy, c->win, False, StructureNotifyMask, (XEvent *)&ev); + XSync(W->dpy, False); +} + +struct client* +client_gb_win(Window w) +{ + struct client *c = SLIST_FIRST(&W->h.client); + + while(c && c->win != w) + c = SLIST_NEXT(c, next); + + return c; +} + +struct client* +client_gb_pos(struct tag *t, int x, int y) +{ + struct client *c = SLIST_FIRST(&t->clients); + + while(c) + { + if(INAREA(x, y, c->geo)) + return c; + + c = SLIST_NEXT(c, tnext); + } + + return NULL; +} + +/** Get client left/right/top/bottom of selected client + *\param bc Base client + *\param pos Position + *\return Client found or NULL +*/ +struct client* +client_next_with_pos(struct client *bc, Position p) +{ + struct client *c; + int x, y; + static const char scanfac[PositionLast] = { +10, -10, 0, 0 }; + Position ip = Bottom - p; + + /* + * Set start place of pointer (edge with position + * of base client) for faster scanning. + */ + x = bc->geo.x + ((p == Right) ? bc->geo.w : 0); + y = bc->geo.y + ((p == Bottom) ? bc->geo.h : 0); + y += ((LDIR(p)) ? (bc->geo.h >> 1) : 0); + x += ((p > Left) ? (bc->geo.w >> 1) : 0); + + /* Scan in right direction to next(p) physical client */ + while((c = client_gb_pos(bc->tag, x, y)) == bc) + { + x += scanfac[p]; + y += scanfac[ip]; + } + + return c; +} + +void +client_swap(struct client *c1, struct client *c2) +{ + struct tag *t; + struct geo g; + + if(c1 == c2 || !c1 || !c2) + return; + + t = c1->tag; + g = c1->geo; + + swap_ptr((void**)&c1->screen, (void**)&c2->screen); + + tag_client(c2->tag, c1); + tag_client(t, c2); + + client_moveresize(c1, c2->geo); + client_moveresize(c2, g); +} + +static void +client_grabbuttons(struct client *c, bool focused) +{ + XUngrabButton(W->dpy, AnyButton, AnyModifier, c->win); + + if(focused) + { + int i = 0; + + while(i++ != Button5) + { + XGrabButton(W->dpy, i, CLIENT_MOUSE_MOD, c->win, False, + ButtonMask, GrabModeAsync, GrabModeSync, None, None); + XGrabButton(W->dpy, i, CLIENT_MOUSE_MOD | LockMask, c->win, False, + ButtonMask, GrabModeAsync, GrabModeSync, None, None); + XGrabButton(W->dpy, i, CLIENT_MOUSE_MOD | W->numlockmask, c->win, False, + ButtonMask, GrabModeAsync, GrabModeSync, None, None); + XGrabButton(W->dpy, i, CLIENT_MOUSE_MOD | LockMask | W->numlockmask, c->win, False, + ButtonMask, GrabModeAsync, GrabModeSync, None, None); + } + + return; + } + + XGrabButton(W->dpy, AnyButton, AnyModifier, c->win, False, + ButtonMask, GrabModeAsync, GrabModeSync, None, None); + +} + +static inline void +client_draw_bord(struct client *c) +{ + struct geo g = { 0, 0, c->screen->ugeo.w, c->screen->ugeo.h }; + + draw_rect(c->tag->frame, g, THEME_DEFAULT->client_n.bg); + + /* Selected client's border */ + if(W->client) + draw_rect(W->client->tag->frame, W->client->tag->sel->geo, THEME_DEFAULT->client_s.bg); +} + + +void +client_focus(struct client *c) +{ + /* Unfocus selected */ + if(W->client && W->client != c) + client_grabbuttons(W->client, false); + + /* Focus c */ + if((W->client = c)) + { + c->tag->sel = c; + + client_draw_bord(c); + client_grabbuttons(c, true); + + XSetInputFocus(W->dpy, c->win, RevertToPointerRoot, CurrentTime); + } + else + { + W->client = W->screen->seltag->sel = NULL; + XSetInputFocus(W->dpy, W->root, RevertToPointerRoot, CurrentTime); + } +} + +/** Get a client name + * \param c struct client pointer +*/ +void +client_get_name(struct client *c) +{ + Atom rt; + int rf; + unsigned long ir, il; + + /* This one instead XFetchName for utf8 name support */ + if(XGetWindowProperty(W->dpy, c->win, ATOM("_NET_WM_NAME"), 0, 4096, + False, ATOM("UTF8_STRING"), &rt, &rf, &ir, &il, (unsigned char**)&c->title) != Success) + XGetWindowProperty(W->dpy, c->win, ATOM("WM_NAME"), 0, 4096, + False, ATOM("UTF8_STRING"), &rt, &rf, &ir, &il, (unsigned char**)&c->title); + + /* Still no title... */ + if(!c->title) + XFetchName(W->dpy, c->win, &(c->title)); +} + +/** Close a client + * \param c struct client pointer +*/ +void +client_close(struct client *c) +{ + int proto; + XEvent ev; + Atom *atom = NULL; + + /* Event will call client_remove */ + if(XGetWMProtocols(W->dpy, c->win, &atom, &proto) && atom) + { + while(proto--) + if(atom[proto] == ATOM("WM_DELETE_WINDOW")) + { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = ATOM("WM_PROTOCOLS"); + ev.xclient.format = 32; + ev.xclient.data.l[0] = ATOM("WM_DELETE_WINDOW"); + ev.xclient.data.l[1] = CurrentTime; + + XSendEvent(W->dpy, c->win, False, NoEventMask, &ev); + XFree(atom); + + return; + } + } + + XKillClient(W->dpy, c->win); +} + +void +uicb_client_close(Uicb cmd) +{ + (void)cmd; + + if(W->client) + client_close(W->client); +} + +struct client* +client_new(Window w, XWindowAttributes *wa) +{ + struct client *c = xcalloc(1, sizeof(struct client)); + + /* C attributes */ + c->win = w; + c->screen = W->screen; + c->flags = 0; + c->tag = NULL; + + /* struct geometry */ + c->geo.x = wa->x; + c->geo.y = wa->y; + c->geo.w = wa->width; + c->geo.h = wa->height; + c->tgeo = c->wgeo = c->geo; + + /* Set tag */ + tag_client(W->screen->seltag, c); + + /* X window attributes */ + XSelectInput(W->dpy, w, EnterWindowMask | LeaveWindowMask | StructureNotifyMask | PropertyChangeMask); + XSetWindowBorderWidth(W->dpy, w, 0); + client_grabbuttons(c, false); + + /* Attach */ + SLIST_INSERT_HEAD(&W->h.client, c, next); + + /* Map */ + WIN_STATE(w, Map); + ewmh_set_wm_state(w, NormalState); + + client_get_name(c); + client_focus(c); + client_configure(c); + + return c; +} + +void +client_moveresize(struct client *c, struct geo g) +{ + int bord = THEME_DEFAULT->client_border_width; + + c->geo = g; + + /* Window geo */ + c->wgeo.x = g.x + bord; + c->wgeo.y = g.y + bord ; + c->wgeo.w = g.w - (bord << 1); + c->wgeo.h = g.h - (bord << 1); + + XMoveResizeWindow(W->dpy, c->win, + c->wgeo.x, c->wgeo.y, + c->wgeo.w, c->wgeo.h); + + client_draw_bord(c); + client_configure(c); +} + +void +client_maximize(struct client *c) +{ + c->geo = c->tag->screen->ugeo; + + c->geo.x = c->geo.y = 0; /* Frame x/y, not screen geo */ + c->geo.w = c->tag->screen->ugeo.w; + c->geo.h = c->tag->screen->ugeo.h; + + client_moveresize(c, c->geo); +} + +void +client_fac_resize(struct client *c, Position p, int fac) +{ + struct client *gc = client_next_with_pos(c, p); + Position rp = RPOS(p); + + if(!gc || gc->screen != c->screen) + return; + + /* Check futur size/pos */ + if(!client_fac_geo(c, p, fac) + || !client_fac_geo(gc, rp, -fac) + || !client_fac_check_row(c, p, fac) + || !client_fac_check_row(gc, rp, -fac)) + return; + + + /* Simple resize with only c & gc */ + if(GEO_CHECK2(c->geo, gc->geo, p)) + { + client_moveresize(c, c->tgeo); + client_moveresize(gc, gc->tgeo); + } + /* Resize with row parents */ + else + { + client_fac_arrange_row(c, p, fac); + client_fac_arrange_row(gc, rp, -fac); + } +} + +void +client_remove(struct client *c) +{ + XGrabServer(W->dpy); + XSetErrorHandler(wmfs_error_handler_dummy); + XReparentWindow(W->dpy, c->win, W->root, c->geo.x, c->geo.y); + + /* Remove from global client list */ + SLIST_REMOVE(&W->h.client, c, client, next); + + tag_client(NULL, c); + + ewmh_set_wm_state(c->win, WithdrawnState); + + XUngrabServer(W->dpy); + XSync(W->dpy, False); + XSetErrorHandler(wmfs_error_handler); + + free(c); +} + +void +client_free(void) +{ + FREE_LIST(client, W->h.client); +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..fce8ed2 --- /dev/null +++ b/src/client.h @@ -0,0 +1,130 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef CLIENT_H +#define CLIENT_H + +#include "wmfs.h" +#include "layout.h" + +void client_configure(struct client *c); +struct client *client_gb_win(Window w); +struct client *client_gb_pos(struct tag *t, int x, int y); +struct client *client_next_with_pos(struct client *bc, Position p); +void client_swap(struct client *c1, struct client *c2); +void client_focus(struct client *c); +void client_get_name(struct client *c); +void client_close(struct client *c); +void uicb_client_close(Uicb cmd); +struct client *client_new(Window w, XWindowAttributes *wa); +void client_moveresize(struct client *c, struct geo g); +void client_maximize(struct client *c); +void client_fac_resize(struct client *c, Position p, int fac); +void client_remove(struct client *c); +void client_free(void); + +/* Generated */ +void uicb_client_resize_Right(Uicb); +void uicb_client_resize_Left(Uicb); +void uicb_client_resize_Top(Uicb); +void uicb_client_resize_Bottom(Uicb); +void uicb_client_focus_Right(Uicb); +void uicb_client_focus_Left(Uicb); +void uicb_client_focus_Top(Uicb); +void uicb_client_focus_Bottom(Uicb); +void uicb_client_swapsel_Right(Uicb); +void uicb_client_swapsel_Left(Uicb); +void uicb_client_swapsel_Top(Uicb); +void uicb_client_swapsel_Bottom(Uicb); +void uicb_client_focus_next(Uicb); +void uicb_client_focus_prev(Uicb); +void uicb_client_swapsel_next(Uicb); +void uicb_client_swapsel_prev(Uicb); + +static inline struct client* +client_next(struct client *c) +{ + return (SLIST_NEXT(c, tnext) + ? SLIST_NEXT(c, tnext) + : SLIST_FIRST(&c->tag->clients)); +} + +static inline struct client* +client_prev(struct client *c) +{ + struct client *cc; + + for(cc = SLIST_FIRST(&c->tag->clients); + SLIST_NEXT(cc, tnext) && SLIST_NEXT(cc, tnext) != c; + cc = SLIST_NEXT(cc, tnext)); + + return cc; +} + +static inline bool +client_fac_geo(struct client *c, Position p, int fac) +{ + struct geo cg = c->geo; + + switch(p) + { + default: + case Right: + cg.w += fac; + break; + case Left: + cg.x -= fac; + cg.w += fac; + break; + case Top: + cg.y -= fac; + cg.h += fac; + break; + case Bottom: + cg.h += fac; + break; + } + + /* Check for incompatible geo */ + if(cg.w > c->screen->ugeo.w || cg.h > c->screen->ugeo.h + || cg.w < 5 || cg.h < 5) + return false; + + /* Set transformed geo in tmp geo */ + c->tgeo = cg; + + return true; +} + +static inline bool +client_fac_check_row(struct client *c, Position p, int fac) +{ + struct geo g = c->geo; + struct client *cc; + + /* Travel clients to search parents of row and check geos */ + SLIST_FOREACH(cc, &c->tag->clients, tnext) + if(GEO_PARENTROW(g, cc->geo, p) && !client_fac_geo(cc, p, fac)) + return false; + + return true; +} + +static inline void +client_fac_arrange_row(struct client *c, Position p, int fac) +{ + struct geo g = c->geo; + struct client *cc; + + /* Travel clients to search row parents and apply fac */ + SLIST_FOREACH(cc, &c->tag->clients, tnext) + if(GEO_PARENTROW(g, cc->geo, p)) + { + client_fac_geo(cc, p, fac); + client_moveresize(cc, cc->tgeo); + } +} + +#endif /* CLIENT_H */ diff --git a/src/client.o b/src/client.o new file mode 100644 index 0000000000000000000000000000000000000000..80c4a454ab1eece3d9ef6de71605603c0abfd064 GIT binary patch literal 19776 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 literal 0 HcmV?d00001 diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..d62f016 --- /dev/null +++ b/src/config.c @@ -0,0 +1,216 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include "config.h" +#include "wmfs.h" +#include "parse.h" +#include "tag.h" +#include "screen.h" +#include "infobar.h" +#include "util.h" + +#define CONFIG_DEFAULT_PATH ".config/wmfs/wmfsrc2" /* tmp */ + +static void +config_theme(void) +{ + struct theme *t, *p = NULL; + size_t i, n; + struct conf_sec *sec, **ks; + + /* [themes] */ + sec = fetch_section_first(NULL, "themes"); + ks = fetch_section(sec, "theme"); + + /* No theme section? Make one with default value anyway. */ + if(!(n = fetch_section_count(ks))) + ++n; + + SLIST_INIT(&W->h.theme); + + /* [theme]*/ + for(i = 0; i < n; ++i) + { + t = (struct theme*)xcalloc(1, sizeof(struct theme)); + + t->name = fetch_opt_first(ks[i], "default", "name").str; + + wmfs_init_font(fetch_opt_first(ks[i], "fixed", "font").str, t); + + /* bars */ + t->bars.fg = color_atoh(fetch_opt_first(ks[i], "#CCCCCC", "bars_fg").str); + t->bars.bg = color_atoh(fetch_opt_first(ks[i], "#222222", "bars_bg").str); + t->bars_width = fetch_opt_first(ks[i], "12", "bars_width").num; + + /* + * Elements + */ + t->tags_n.fg = color_atoh(fetch_opt_first(ks[i], "#CCCCCC", "tags_normal_fg").str); + t->tags_n.bg = color_atoh(fetch_opt_first(ks[i], "#222222", "tags_normal_bg").str); + t->tags_s.fg = color_atoh(fetch_opt_first(ks[i], "#222222", "tags_sel_fg").str); + t->tags_s.bg = color_atoh(fetch_opt_first(ks[i], "#CCCCCC", "tags_sel_bg").str); + t->tags_border_col = color_atoh(fetch_opt_first(ks[i], "#888888", "tags_border_color").str); + t->tags_border_width = fetch_opt_first(ks[i], "0", "tags_border_width").num; + + /* Client / frame */ + t->client_n.fg = color_atoh(fetch_opt_first(ks[i], "#CCCCCC", "client_normal_fg").str); + t->client_n.bg = color_atoh(fetch_opt_first(ks[i], "#222222", "client_normal_bg").str); + t->client_s.fg = color_atoh(fetch_opt_first(ks[i], "#222222", "client_sel_fg").str); + t->client_s.bg = color_atoh(fetch_opt_first(ks[i], "#CCCCCC", "client_sel_bg").str); + t->frame_bg = color_atoh(fetch_opt_first(ks[i], "#555555", "frame_bg").str); + t->client_titlebar_width = fetch_opt_first(ks[i], "12", "client_titlebar_width").num; + t->client_border_width = fetch_opt_first(ks[i], "1", "client_border_width").num; + + /* insert_tail with SLIST */ + if(SLIST_EMPTY(&W->h.theme)) + SLIST_INSERT_HEAD(&W->h.theme, t, next); + else + SLIST_INSERT_AFTER(p, t, next); + + p = t; + } + + free(ks); +} + +static void +config_bars(void) +{ + struct screen *s; + struct theme *t; + size_t i, n; + struct conf_sec *sec, **ks; + int screenid; + char *elem; + Barpos pos = BarTop; + + /* [bars] */ + sec = fetch_section_first(NULL, "bars"); + ks = fetch_section(sec, "bar"); + n = fetch_section_count(ks); + + /* [bar] */ + for(i = 0; i < n; ++i) + { + elem = fetch_opt_first(ks[i], "", "elements").str; + screenid = fetch_opt_first(ks[i], "-1", "screen").num; + t = name_to_theme(fetch_opt_first(ks[i], "default", "theme").str); + pos = fetch_opt_first(ks[i], "0", "position").num; + + SLIST_FOREACH(s, &W->h.screen, next) + if(screenid == s->id || screenid == -1) + infobar_new(s, t, pos, elem); + } + + free(ks); +} + + +static void +config_tag(void) +{ + struct screen *s; + struct tag *t; + size_t i, n; + struct conf_sec *sec, **ks; + char *name; + int screenid; + + /* [tags] */ + sec = fetch_section_first(NULL, "tags"); + ks = fetch_section(sec, "tag"); + n = fetch_section_count(ks); + + /* [tag] */ + for(i = 0; i < n; ++i) + { + name = fetch_opt_first(ks[i], "tag", "name").str; + screenid = fetch_opt_first(ks[i], "-1", "screen").num; + + SLIST_FOREACH(s, &W->h.screen, next) + if(screenid == s->id || screenid == -1) + { + t = tag_new(s, name); + + /* Set first tag as seltag */ + if(t == TAILQ_FIRST(&s->tags)) + s->seltag = t; + } + } + + free(ks); +} + +static void +config_keybind(void) +{ + int i, n; + size_t j; + struct conf_sec *sec, **ks; + struct opt_type *opt; + char *cmd; + struct keybind *k; + + /* [keys] */ + sec = fetch_section_first(NULL, "keys"); + ks = fetch_section(sec, "key"); + n = fetch_section_count(ks); + + SLIST_INIT(&W->h.keybind); + + /* [key] */ + for(i = 0; i < n; ++i) + { + k = (struct keybind*)xcalloc(1, sizeof(struct keybind)); + + /* mod = {} */ + opt = fetch_opt(ks[i], "", "mod"); + + for(j = k->mod = 0; j < fetch_opt_count(opt); ++j) + k->mod |= modkey_keysym(opt[j].str); + + free(opt); + + /* key = */ + k->keysym = XStringToKeysym(fetch_opt_first(ks[i], "None", "key").str); + + /* func = */ + if(!(k->func = uicb_name_func(fetch_opt_first(ks[i], "", "func").str))) + { + warnx("configuration: Unknown Function \"%s\".", + fetch_opt_first(ks[i], "", "func").str); + k->func = uicb_spawn; + } + + /* cmd = */ + if((cmd = fetch_opt_first(ks[i], "", "cmd").str)) + k->cmd = xstrdup(cmd); + + SLIST_INSERT_HEAD(&W->h.keybind, k, next); + } + + wmfs_grab_keys(); + + free(ks); +} + +void +config_init(void) +{ + char *path; + + xasprintf(&path, "%s/"CONFIG_DEFAULT_PATH, getenv("HOME")); + + if(get_conf(path) == -1) + errx(1, "parsing configuration file (%s) failed.", path); + + config_theme(); + config_keybind(); + config_tag(); + config_bars(); + + free(path); + free_conf(); +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..998bd2a --- /dev/null +++ b/src/config.h @@ -0,0 +1,115 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include + +#include "wmfs.h" +#include "util.h" +#include "tag.h" +#include "client.h" + +#define THEME_DEFAULT (SLIST_FIRST(&W->h.theme)) + +static const struct { char *name; void (*func)(Uicb cmd); } uicb_list[] = +{ + /* Sys */ + { "spawn", uicb_spawn }, + { "quit", uicb_quit }, + { "reload", uicb_reload }, + + /* Tag */ + { "tag_set", uicb_tag_set }, + { "tag", uicb_tag_set_with_name }, + { "tag_next", uicb_tag_next }, + { "tag_prev", uicb_tag_prev }, + + /* Layout */ + { "layout_vmirror", uicb_layout_vmirror }, + { "layout_hmirror", uicb_layout_hmirror }, + { "layout_rotate_left", uicb_layout_rotate_left }, + { "layout_rotate_right", uicb_layout_rotate_right }, + + /* Client */ + { "client_close", uicb_client_close }, + { "client_resize_right", uicb_client_resize_Right }, + { "client_resize_left", uicb_client_resize_Left }, + { "client_resize_top", uicb_client_resize_Top }, + { "client_resize_bottom", uicb_client_resize_Bottom }, + { "client_focus_right", uicb_client_focus_Right }, + { "client_focus_left", uicb_client_focus_Left }, + { "client_focus_top", uicb_client_focus_Top }, + { "client_focus_bottom", uicb_client_focus_Bottom }, + { "client_swap_right", uicb_client_swapsel_Right }, + { "client_swap_left", uicb_client_swapsel_Left }, + { "client_swap_top", uicb_client_swapsel_Top }, + { "client_swap_bottom", uicb_client_swapsel_Bottom }, + { "client_focus_next", uicb_client_focus_next }, + { "client_focus_prev", uicb_client_focus_prev }, + { "client_swap_next", uicb_client_swapsel_next }, + { "client_swap_prev", uicb_client_swapsel_prev }, + { NULL, NULL } +}; + +static inline void* +uicb_name_func(Uicb name) +{ + int i = 0; + + for(; uicb_list[i].func; ++i) + if(!strcmp(name, uicb_list[i].name)) + return uicb_list[i].func; + + return NULL; +} + +static const struct { const char *name; KeySym keysym; } key_list[] = +{ + {"Control", ControlMask }, + {"Shift", ShiftMask }, + {"Lock", LockMask }, + {"Alt", Mod1Mask }, + {"Mod1", Mod1Mask }, + {"Mod2", Mod2Mask }, + {"Mod3", Mod3Mask }, + {"Mod4", Mod4Mask }, + {"Super", Mod4Mask }, + {"Home", Mod4Mask }, + {"Mod5", Mod5Mask }, + {NULL, NoSymbol } +}; + +static inline KeySym +modkey_keysym(const char *name) +{ + int i = 0; + + for(; key_list[i].name; ++i) + if(!strcmp(name, key_list[i].name)) + return key_list[i].keysym; + + return NoSymbol; +} + +static inline struct theme* +name_to_theme(const char *name) +{ + struct theme *t; + + SLIST_FOREACH(t, &W->h.theme, next) + if(!strcmp(t->name, name)) + return t; + + return THEME_DEFAULT; +} + + +void config_init(void); + +#endif /* CONFIG_H */ diff --git a/src/config.o b/src/config.o new file mode 100644 index 0000000000000000000000000000000000000000..8ff4fde531380470c6831f54956a00e857404489 GIT binary patch literal 15696 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 C0WWa? literal 0 HcmV?d00001 diff --git a/src/draw.h b/src/draw.h new file mode 100644 index 0000000..f4cb968 --- /dev/null +++ b/src/draw.h @@ -0,0 +1,42 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + + +#ifndef DRAW_H +#define DRAW_H + +#include +#include + +#include "wmfs.h" + +#define TEXTY(t, w) ((t->font.height - t->font.de) + ((w - t->font.height) >> 1)) +#define PAD (8) + +static inline void +draw_text(Drawable d, struct theme *t, int x, int y, Color fg, const char *str) +{ + XSetForeground(W->dpy, W->gc, fg); + XmbDrawString(W->dpy, d, t->font.fontset, W->gc, x, y, str, strlen(str)); +} + +static inline void +draw_rect(Drawable d, struct geo g, Color bg) +{ + XSetForeground(W->dpy, W->gc, bg); + XFillRectangle(W->dpy, d, W->gc, g.x, g.y, g.w, g.h); +} + +static inline unsigned short +draw_textw(struct theme *t, const char *str) +{ + XRectangle r; + + XmbTextExtents(t->font.fontset, str, strlen(str), NULL, &r); + + return r.width; +} + +#endif /* DRAW_H */ diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..e76ccde --- /dev/null +++ b/src/event.c @@ -0,0 +1,260 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include "event.h" +#include "ewmh.h" +#include "util.h" +#include "wmfs.h" +#include "client.h" +#include "barwin.h" +#include "screen.h" + +#define EVDPY(e) (e)->xany.display + +static void +event_buttonpress(XEvent *e) +{ + XButtonEvent *ev = &e->xbutton; + struct mousebind *m; + struct barwin *b; + + screen_update_sel(); + + SLIST_FOREACH(b, &W->h.barwin, next) + if(b->win == ev->window) + { + SLIST_FOREACH(m, &b->mousebinds, next) + if(m->button == ev->button) + if(!m->use_area || (m->use_area && INAREA(ev->x, ev->y, m->area))) + if(m->func) + m->func(m->cmd); + + break; + } +} + +static void +event_enternotify(XEvent *e) +{ + XCrossingEvent *ev = &e->xcrossing; + struct client *c; + + if((ev->mode != NotifyNormal + || ev->detail == NotifyInferior + || ev->detail == NotifyAncestor) + && ev->window != W->root) + return; + + if((c = client_gb_win(ev->window))) + client_focus(c); +} + +static void +event_clientmessageevent(XEvent *e) +{ + (void)e; + /* XClientMessageEvent *ev = &e->xclient; + client *c;*/ +} + +static void +event_configureevent(XEvent *e) +{ + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + struct client *c; + + if((c = client_gb_win(ev->window))) + { + if(ev->value_mask & CWX) + c->geo.x = ev->x; + if(ev->value_mask & CWY) + c->geo.y = ev->y; + if(ev->value_mask & CWWidth) + c->geo.w = ev->width; + if(ev->value_mask & CWHeight) + c->geo.h = ev->height; + + client_configure(c); + + XMoveResizeWindow(EVDPY(e), c->win, c->geo.x, c->geo.y, c->geo.w, c->geo.h); + } + else + { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + + XConfigureWindow(EVDPY(e), ev->window, ev->value_mask, &wc); + } +} + +static void +event_destroynotify(XEvent *e) +{ + XDestroyWindowEvent *ev = &e->xdestroywindow; + struct client *c; + + if((c = client_gb_win(ev->window))) + client_remove(c); +} + +static void +event_focusin(XEvent *e) +{ + if(W->client && e->xfocus.window != W->client->win) + client_focus(W->client); +} + +static void +event_maprequest(XEvent *e) +{ + XMapRequestEvent *ev = &e->xmaprequest; + XWindowAttributes at; + + /* Which windows to manage */ + if(!XGetWindowAttributes(EVDPY(e), ev->window, &at) + || at.override_redirect) + return; + + if(!client_gb_win(ev->window)) + client_new(ev->window, &at); +} + +static void +event_mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + XRefreshKeyboardMapping(ev); + + if(ev->request == MappingKeyboard) + wmfs_grab_keys(); +} + +static void +event_propertynotify(XEvent *e) +{ + XPropertyEvent *ev = &e->xproperty; + struct client *c; + + if(ev->state == PropertyDelete) + return; + + if((c = client_gb_win(ev->window))) + { + switch(ev->atom) + { + case XA_WM_TRANSIENT_FOR: + break; + case XA_WM_NORMAL_HINTS: + /* client_get_size_hints(c); */ + break; + case XA_WM_HINTS: + /* + XWMHints *h; + + if((h = XGetWMHints(EVDPY, c->win)) && (h->flags & XUrgencyHint) && c != sel) + { + client_urgent(c, True); + XFree(h); + } + */ + break; + default: + if(ev->atom == XA_WM_NAME || ev->atom == W->net_atom[net_wm_name]) + client_get_name(c); + break; + } + } +} + +static void +event_unmapnotify(XEvent *e) +{ + XUnmapEvent *ev = &e->xunmap; + struct client *c; + + if((c = client_gb_win(ev->window)) && ev->send_event) + client_remove(c); +} + +/* +static void +event_motionnotify(XEvent *e) +{ + XMotionEvent *ev = &e->xmotion; + struct client *c; + + if((c = client_gb_win(ev->subwindow)) && c != c->tag->sel) + client_focus(c); +} +*/ + +static void +event_keypress(XEvent *e) +{ + XKeyPressedEvent *ev = &e->xkey; + KeySym keysym = XKeycodeToKeysym(EVDPY(e), (KeyCode)ev->keycode, 0); + struct keybind *k; + + screen_update_sel(); + + SLIST_FOREACH(k, &W->h.keybind, next) + if(k->keysym == keysym && KEYPRESS_MASK(k->mod) == KEYPRESS_MASK(ev->state)) + if(k->func) + k->func(k->cmd); +} + +static void +event_expose(XEvent *e) +{ + XExposeEvent *ev = &e->xexpose; + struct barwin *b; + + SLIST_FOREACH(b, &W->h.barwin, next) + if(b->win == ev->window) + { + barwin_refresh(b); + return; + } +} + +static void +event_dummy(XEvent *e) +{ + /* printf("%d\n", e->type);*/ + (void)e; +} + +void +event_init(void) +{ + int i = MAX_EV; + + while(i--) + event_handle[i] = event_dummy; + + event_handle[ButtonPress] = event_buttonpress; + event_handle[ClientMessage] = event_clientmessageevent; + event_handle[ConfigureRequest] = event_configureevent; + event_handle[DestroyNotify] = event_destroynotify; + event_handle[EnterNotify] = event_enternotify; + event_handle[Expose] = event_expose; + event_handle[FocusIn] = event_focusin; + event_handle[KeyPress] = event_keypress; + /*event_handle[MapNotify] = event_mapnotify;*/ + event_handle[MapRequest] = event_maprequest; + event_handle[MappingNotify] = event_mappingnotify; + /*event_handle[MotionNotify] = event_motionnotify;*/ + event_handle[PropertyNotify] = event_propertynotify; + /*event_handle[ReparentNotify] = event_reparentnotify;*/ + /*event_handle[SelectionClear] = event_selectionclearevent;*/ + event_handle[UnmapNotify] = event_unmapnotify; +} + diff --git a/src/event.h b/src/event.h new file mode 100644 index 0000000..b43e2e8 --- /dev/null +++ b/src/event.h @@ -0,0 +1,20 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef EVENT_H +#define EVENT_H + +#include "wmfs.h" + +#define MAX_EV 256 + +#define KEYPRESS_MASK(m) (m & ~(W->numlockmask | LockMask)) +#define EVENT_HANDLE(e) event_handle[(e)->type](e); + +void event_init(void); + +void (*event_handle[MAX_EV])(XEvent*); + +#endif /* EVENT_H */ diff --git a/src/event.o b/src/event.o new file mode 100644 index 0000000000000000000000000000000000000000..76b12c4b6c61df9f1d0e414e4fa547d008a2c40b GIT binary patch literal 7032 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 literal 0 HcmV?d00001 diff --git a/src/ewmh.c b/src/ewmh.c new file mode 100644 index 0000000..00c379c --- /dev/null +++ b/src/ewmh.c @@ -0,0 +1,106 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include +#include + +#include "ewmh.h" +#include "util.h" + +void +ewmh_init(void) +{ + int b = 1; + + W->net_atom = xcalloc(net_last, sizeof(Atom)); + + /* EWMH hints */ + W->net_atom[wm_state] = ATOM("WM_STATE"); + W->net_atom[net_supported] = ATOM("_NET_SUPPORTED"); + W->net_atom[net_client_list] = ATOM("_NET_CLIENT_LIST"); + W->net_atom[net_frame_extents] = ATOM("_NET_FRAME_EXTENTS"); + W->net_atom[net_number_of_desktops] = ATOM("_NET_NUMBER_OF_DESKTOPS"); + W->net_atom[net_current_desktop] = ATOM("_NET_CURRENT_DESKTOP"); + W->net_atom[net_desktop_names] = ATOM("_NET_DESKTOP_NAMES"); + W->net_atom[net_desktop_geometry] = ATOM("_NET_DESKTOP_GEOMETRY"); + W->net_atom[net_active_window] = ATOM("_NET_ACTIVE_WINDOW"); + W->net_atom[net_close_window] = ATOM("_NET_CLOSE_WINDOW"); + W->net_atom[net_wm_name] = ATOM("_NET_WM_NAME"); + W->net_atom[net_wm_pid] = ATOM("_NET_WM_PID"); + W->net_atom[net_wm_desktop] = ATOM("_NET_WM_DESKTOP"); + W->net_atom[net_showing_desktop] = ATOM("_NET_SHOWING_DESKTOP"); + W->net_atom[net_wm_icon_name] = ATOM("_NET_WM_ICON_NAME"); + W->net_atom[net_wm_window_type] = ATOM("_NET_WM_WINDOW_TYPE"); + W->net_atom[net_supporting_wm_check] = ATOM("_NET_SUPPORTING_WM_CHECK"); + W->net_atom[net_wm_window_opacity] = ATOM("_NET_WM_WINDOW_OPACITY"); + W->net_atom[net_wm_window_type_normal] = ATOM("_NET_WM_WINDOW_TYPE_NORMAL"); + W->net_atom[net_wm_window_type_dock] = ATOM("_NET_WM_WINDOW_TYPE_DOCK"); + W->net_atom[net_wm_window_type_splash] = ATOM("_NET_WM_WINDOW_TYPE_SPLASH"); + W->net_atom[net_wm_window_type_dialog] = ATOM("_NET_WM_WINDOW_TYPE_DIALOG"); + W->net_atom[net_wm_icon] = ATOM("_NET_WM_ICON"); + W->net_atom[net_wm_state] = ATOM("_NET_WM_STATE"); + W->net_atom[net_wm_state_fullscreen] = ATOM("_NET_WM_STATE_FULLSCREEN"); + W->net_atom[net_wm_state_sticky] = ATOM("_NET_WM_STATE_STICKY"); + W->net_atom[net_wm_state_demands_attention] = ATOM("_NET_WM_STATE_DEMANDS_ATTENTION"); + W->net_atom[net_wm_system_tray_opcode] = ATOM("_NET_SYSTEM_TRAY_OPCODE"); + W->net_atom[net_system_tray_message_data] = ATOM("_NET_SYSTEM_TRAY_MESSAGE_DATA"); + W->net_atom[net_system_tray_visual] = ATOM("_NET_SYSTEM_TRAY_VISUAL"); + W->net_atom[net_system_tray_orientation] = ATOM("_NET_SYSTEM_TRAY_ORIENTATION"); + W->net_atom[xembed] = ATOM("_XEMBED"); + W->net_atom[xembedinfo] = ATOM("_XEMBED_INFO"); + W->net_atom[manager] = ATOM("MANAGER"); + W->net_atom[utf8_string] = ATOM("UTF8_STRING"); + + /* WMFS hints */ + W->net_atom[wmfs_running] = ATOM("_WMFS_RUNNING"); + W->net_atom[wmfs_update_hints] = ATOM("_WMFS_UPDATE_HINTS"); + W->net_atom[wmfs_set_screen] = ATOM("_WMFS_SET_SCREEN"); + W->net_atom[wmfs_screen_count] = ATOM("_WMFS_SCREEN_COUNT"); + W->net_atom[wmfs_current_tag] = ATOM("_WMFS_CURRENT_TAG"); + W->net_atom[wmfs_tag_list] = ATOM("_WMFS_TAG_LIST"); + W->net_atom[wmfs_current_screen] = ATOM("_WMFS_CURRENT_SCREEN"); + W->net_atom[wmfs_current_layout] = ATOM("_WMFS_CURRENT_LAYOUT"); + W->net_atom[wmfs_mwfact] = ATOM("_WMFS_MWFACT"); + W->net_atom[wmfs_nmaster] = ATOM("_WMFS_NMASTER"); + W->net_atom[wmfs_function] = ATOM("_WMFS_FUNCTION"); + W->net_atom[wmfs_cmd] = ATOM("_WMFS_CMD"); + W->net_atom[wmfs_font] = ATOM("_WMFS_FONT"); + + XChangeProperty(W->dpy, W->root, W->net_atom[net_supported], XA_ATOM, 32, + PropModeReplace, (unsigned char*)W->net_atom, net_last); + + XChangeProperty(W->dpy, W->root, W->net_atom[wmfs_running], XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&b, 1); + + /* Set _NET_SUPPORTING_WM_CHECK */ + XChangeProperty(W->dpy, W->root, W->net_atom[net_supporting_wm_check], XA_WINDOW, 32, + PropModeReplace, (unsigned char*)&W->root, 1); + /* + XChangeProperty(W->dpy, W->root, W->net_atom[net_wm_name], W->net_atom[utf8_string], 8, + PropModeReplace, (unsigned char*)&rootn, strlen(rootn)); + + XChangeProperty(W->dpy, W->root, ATOM("WM_CLASS"), XA_STRING, 8, + PropModeReplace, (unsigned char*)&class, strlen(class)); + + * Set _NET_WM_PID + XChangeProperty(W->dpy, W->root, W->net_atom[net_wm_pid], XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&pid, 1); + + * Set _NET_SHOWING_DESKTOP + XChangeProperty(W->dpy, W->root, W->net_atom[net_showing_desktop], XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&showing_desk, 1); + */ + +} + +void +ewmh_set_wm_state(Window w, int state) +{ + unsigned char d[] = { state, None }; + + XChangeProperty(W->dpy, w, W->net_atom[wm_state], + W->net_atom[wm_state], 32, PropModeReplace, d, 2); +} + diff --git a/src/ewmh.h b/src/ewmh.h new file mode 100644 index 0000000..cfadd11 --- /dev/null +++ b/src/ewmh.h @@ -0,0 +1,74 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + + +#ifndef EWMH_H +#define EWMH_H + +#include "wmfs.h" + +/* Ewmh hints list */ +enum +{ + /* ICCCM */ + wm_state, + /* EWMH */ + net_supported, + net_wm_name, + net_client_list, + net_frame_extents, + net_number_of_desktops, + net_current_desktop, + net_desktop_names, + net_desktop_geometry, + net_active_window, + net_close_window, + net_wm_icon_name, + net_wm_window_type, + net_wm_pid, + net_showing_desktop, + net_supporting_wm_check, + net_wm_window_opacity, + net_wm_window_type_normal, + net_wm_window_type_dock, + net_wm_window_type_splash, + net_wm_window_type_dialog, + net_wm_desktop, + net_wm_icon, + net_wm_state, + net_wm_state_fullscreen, + net_wm_state_sticky, + net_wm_state_demands_attention, + net_wm_system_tray_opcode, + net_system_tray_message_data, + net_system_tray_s, + net_system_tray_visual, + net_system_tray_orientation, + xembed, + xembedinfo, + manager, + utf8_string, + /* WMFS HINTS */ + wmfs_running, + wmfs_update_hints, + wmfs_current_tag, + wmfs_current_screen, + wmfs_current_layout, + wmfs_tag_list, + wmfs_mwfact, + wmfs_nmaster, + wmfs_set_screen, + wmfs_screen_count, + wmfs_function, + wmfs_cmd, + wmfs_font, + wmfs_statustext, + net_last +}; + +void ewmh_init(void); +void ewmh_set_wm_state(Window w, int state); + +#endif /* EWMH_H */ diff --git a/src/ewmh.o b/src/ewmh.o new file mode 100644 index 0000000000000000000000000000000000000000..14ece66326d1b533f6c97d733d1e44cbc5769f99 GIT binary patch literal 11400 zcmbW74Qw1o701W1N!>OHCJmKAp6_<`-p8py62{sI3jUe z`uAV zHBYGKq-q{h%?Z^UQ_YfUj;Q8V^YK<)e!<75>hhO;e0NGk z@E8r}eeeVg_xa#sG~DikCu!L7!6#_A!v~+F;gAoWqT!Seo~Ge$A3Q_D13q|`hDUtx zc^WS1uzdCocn%sN{$SOCjc_p%@8BS5!F=vw{k4BXTcpy9` z!Re>+U3Ytq=S76^4#+C;c{Oybaugh~@+#2+?=(M)-OiQ&zny#dGs$h&KGV$WL$Vuw z&D&bg+EN;6myMJUuZOOUzP!L&-}cgu;3q%-um~dFmkzz(Zisgz<1~mgDKvIrEO+6Yd6C~s?!=?vT~=lIa;&mSnDI)w&91EUn($I(S8j+^a`STN z5V&l8u%XP3}w>arAK`WiL7+W@bw^ znW}9@3mq;vHS)sOy zpU;B5)pWY-L?qeeaq^mJpZS*+F zR5%$~oI`j1JpQr2yIMcCg|Na*K z4BQ8laaYIh`i0L4Cv)o6CF6(j0^k?0tcW>YpXm3uq55M+PckR2-&I(@;b59IjI=Z~ zF1xOIg5OKmaD6rU)-LCTCb+$?XgRW^3BF-1mtUEHi|`+$L-2>-Sg=?aDYD@Gy~SP@ zyl-fT1qb(n5gaNGZU}DB%#AEq=yP@q_U-cy{7yoFk+k$s|; z%%jNJRo#v3I#uA-E&TT>cHx?Qc*=Yk*H?P_FV>K2ccR`7Ys!;YGoG*KzZMX*6ySC( z{boXELxbDDT-d*-FSwmS=IrXTOU|GlO&)Zg*w(C%Th z;q?oWoVNq6$>c_t`U9x1z^yLz`%(X#rhgOl^&TDiKZlpZ9hyFk`rVp7iF%Kwmr#fI z5=e4>@~H3E^bqPtG(CX&QI8JuBOf?F(R4$!#@MqS9md5l&WoBphxTtZeHv})m;Ika z{X>l}p?y`;^Jp)}_GO$9>Z>(9fch6TouU3Uj}G&&0mo}=`faFpYkDi{J3YEschvW3 z`ZVf?HGLBG?`e7o^~W_mkNPv39zy*EO%I?xtLZ4Sw>>({!}aL+K+`3^5wA0Fui(Ec z{G}e!X4GL_WDKZtH=#S8IB@Hgadr~?K0>b$aULc3#|eFkh%-&_vxNR05$7KS|6f91 ziH}H_XYc%c7Il4|zd-1B5pg;RzKhUz6LE?J|1hB+C*u5w;GZV+b3~li3H~iYzevRS z55eCE7YR@L{0C6i*Q<@-I|w~a#K{qSfzS^UalS+FM+yDsM4V>`{zXFn0}#~~AHxO|)5xh<4_Y!e-5c~l`e}sth7{NbD=x2#I=Lr5yLjQn> z^KXK`9*^Ss{NIAQeqF33_)UbKAmVH#_+5nlZ6eMQfj-@_5hqIUJ%rv*#MwviWkUY}5$6elKTYVb6LH=k_`ebQRU*z( zJoM=E{24+IqOPykMuN8q{azx@4uU^G=#LO_9wYcC3H>Y)=N!SmN$7tk;#?&7r8m@m zF1Mnt&vSs_ze?yP5hqUYc|so~;v6LSaYFwg5$9(Fe}>RsBjWsy;QvbK9}#gH;DX>u zzb;l0`dZZW^$HTaLFgGG&Nm3YpU_K0oH2s`KB51Dh;xeIX9)d|M4Uep{J#jD!3VJ# z>#~f{R}*>^byzPb%J;Yg!S@h)fzY2qU6219!M{N0zajKL5&BJVxa3K1w}3iKE8dC( zS0wa9g#H8(KOmwp2A31GEt7=>smt$`(*JRD_#6U1Jl0jigs}81_ES3aZ=Y}+f%^Cw zD=~ISt0x`!S|v^KZVQjpk6V8wuM6$V{}d+!%l6-q0_MO%lKHJz*6Yu~#|@rNX&sZ} zm-XSh5tO}a1@(?W$9U> { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include "wmfs.h" +#include "draw.h" +#include "infobar.h" +#include "barwin.h" +#include "util.h" +#include "tag.h" + +static void infobar_elem_tag_init(struct element *e); +static void infobar_elem_tag_update(struct element *e); + +const struct elem_funcs +{ + char c; + void (*func_init)(struct element *e); + void (*func_update)(struct element *e); +} elem_funcs[] = +{ + { 't', infobar_elem_tag_init, infobar_elem_tag_update }, + + /* { 'l', infobar_elem_layout_init, infobar_elem_layout_update }, + { 's', infobar_elem_selbar_init, infobar_elem_selbar_update }, + { 'S', infobar_elem_status_init, infobar_elem_status_update },*/ + + { '\0', NULL, NULL } +}; + +static void +infobar_elem_tag_init(struct element *e) +{ + struct tag *t; + struct barwin *b, *prev = NULL; + struct geo g = { 0, 0, 0, 0 }; + int s, j; + + infobar_elem_placement(e); + + j = e->geo.x; + e->geo.h -= (e->infobar->theme->tags_border_width << 1); + + TAILQ_FOREACH(t, &e->infobar->screen->tags, next) + { + s = draw_textw(e->infobar->theme, t->name) + PAD; + + /* Init barwin */ + b = barwin_new(e->infobar->bar->win, j, 0, s, e->geo.h, 0, 0, false); + + /* Set border */ + if(e->infobar->theme->tags_border_width) + { + XSetWindowBorder(W->dpy, b->win, e->infobar->theme->tags_border_col); + XSetWindowBorderWidth(W->dpy, b->win, e->infobar->theme->tags_border_width); + } + + b->ptr = (void*)t; + barwin_map(b); + + /* TODO: refer to tag element configuration */ + barwin_mousebind_new(b, Button1, false, g, uicb_tag_set_with_name, (Uicb)t->name); + barwin_mousebind_new(b, Button4, false, g, uicb_tag_next, NULL); + barwin_mousebind_new(b, Button5, false, g, uicb_tag_prev, NULL); + + /* insert_tail with SLIST */ + if(SLIST_EMPTY(&e->bars)) + SLIST_INSERT_HEAD(&e->bars, b, enext); + else + SLIST_INSERT_AFTER(prev, b, enext); + + prev = b; + j += s; + } + + e->infobar->screen->elemupdate |= FLAGINT(ElemTag); + + e->geo.w = j; +} + +static void +infobar_elem_tag_update(struct element *e) +{ + struct tag *t, *sel = e->infobar->screen->seltag; + struct barwin *b; + + SLIST_FOREACH(b, &e->bars, enext) + { + t = (struct tag*)b->ptr; + + /* Selected */ + /* TODO: color from conf */ + if(t == sel) + { + b->fg = e->infobar->theme->tags_s.fg; + b->bg = e->infobar->theme->tags_s.bg; + } + else + { + b->fg = e->infobar->theme->tags_n.fg; + b->bg = e->infobar->theme->tags_n.bg; + } + + barwin_refresh_color(b); + + draw_text(b->dr, e->infobar->theme, (PAD >> 1), + TEXTY(e->infobar->theme, e->geo.h), b->fg, t->name); + + barwin_refresh(b); + } +} + +static void +infobar_elem_init(struct infobar *i) +{ + struct element *e; + int n, j; + + TAILQ_INIT(&i->elements); + + for(n = 0; n < (int)strlen(i->elemorder); ++n) + { + for(j = 0; j < (int)LEN(elem_funcs); ++j) + if(elem_funcs[j].c == i->elemorder[n]) + { + e = xcalloc(1, sizeof(struct element)); + + SLIST_INIT(&e->bars); + + e->infobar = i; + e->type = j; + e->func_init = elem_funcs[j].func_init; + e->func_update = elem_funcs[j].func_update; + + TAILQ_INSERT_TAIL(&i->elements, e, next); + + e->func_init(e); + + break; + } + } +} + +void +infobar_elem_update(struct infobar *i) +{ + struct element *e; + + TAILQ_FOREACH(e, &i->elements, next) + if(i->screen->elemupdate & FLAGINT(e->type)) + e->func_update(e); +} + +void +infobar_elem_remove(struct element *e) +{ + struct barwin *b; + + TAILQ_REMOVE(&e->infobar->elements, e, next); + + while(!SLIST_EMPTY(&e->bars)) + { + b = SLIST_FIRST(&e->bars); + SLIST_REMOVE_HEAD(&e->bars, enext); + barwin_remove(b); + } +} + +struct infobar* +infobar_new(struct screen *s, struct theme *theme, Barpos pos, const char *elem) +{ + bool map; + struct infobar *i = (struct infobar*)xcalloc(1, sizeof(struct infobar)); + + i->screen = s; + i->theme = theme; + i->elemorder = xstrdup(elem); + + map = infobar_placement(i, pos); + + /* struct barwin create */ + i->bar = barwin_new(W->root, i->geo.x, i->geo.y, i->geo.w, i->geo.h, + theme->bars.fg, theme->bars.bg, false); + + SLIST_INSERT_HEAD(&s->infobars, i, next); + + /* struct elements */ + infobar_elem_init(i); + + /* Render, only if pos is Top or Bottom */ + if(!map) + return i; + + barwin_map(i->bar); + barwin_map_subwin(i->bar); + barwin_refresh_color(i->bar); + infobar_refresh(i); + + return i; +} + +void +infobar_refresh(struct infobar *i) +{ + infobar_elem_update(i); + + barwin_refresh(i->bar); +} + +void +infobar_remove(struct infobar *i) +{ + struct element *e; + + free(i->elemorder); + + TAILQ_FOREACH(e, &i->elements, next) + infobar_elem_remove(e); + + barwin_remove(i->bar); + + SLIST_REMOVE(&i->screen->infobars, i, infobar, next); + + free(i); +} + +void +infobar_free(struct screen *s) +{ + struct infobar *i; + + while(!SLIST_EMPTY(&s->infobars)) + { + i = SLIST_FIRST(&s->infobars); + + /* SLIST_REMOVE is done by infobar_remove */ + infobar_remove(i); + } +} + + + + + + diff --git a/src/infobar.h b/src/infobar.h new file mode 100644 index 0000000..ea759c4 --- /dev/null +++ b/src/infobar.h @@ -0,0 +1,74 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef INFOBAR_H +#define INFOBAR_H + +#include "wmfs.h" +#include "util.h" +#include "draw.h" +#include "tag.h" + +enum { ElemTag = 0, ElemLayout, ElemSelbar, ElemStatus, ElemCustom, ElemLast }; + +struct infobar *infobar_new(struct screen *s, struct theme *theme, Barpos pos, const char *elem); +void infobar_elem_update(struct infobar *i); +void infobar_refresh(struct infobar *i); +void infobar_remove(struct infobar *i); +void infobar_free(struct screen *s); + +/* Basic placement of elements */ +static inline void +infobar_elem_placement(struct element *e) +{ + struct element *p = TAILQ_PREV(e, esub, next); + + e->geo.y = e->geo.w = 0; + e->geo.h = e->infobar->geo.h; + e->geo.x = (p ? p->geo.x + p->geo.w + PAD : 0); +} + +/* Bars placement management and usable space management */ +static inline bool +infobar_placement(struct infobar *i, Barpos p) +{ + i->pos = p; + i->geo = i->screen->ugeo; + i->geo.h = i->theme->bars_width; + + switch(p) + { + case BarTop: + i->screen->ugeo.y += i->geo.h; + i->screen->ugeo.h -= i->geo.h; + break; + case BarBottom: + i->geo.y = (i->screen->ugeo.y + i->screen->ugeo.h) - i->geo.h; + i->screen->ugeo.h -= i->geo.h; + break; + default: + case BarHide: + return false; + } + + tag_update_frame_geo(i->screen); + + return true; +} + +static inline void +infobar_elem_screen_update(struct screen *s, int addf) +{ + struct infobar *i; + + s->elemupdate |= FLAGINT(addf); + + SLIST_FOREACH(i, &s->infobars, next) + infobar_elem_update(i); + + s->elemupdate &= ~FLAGINT(ElemTag); +} + +#endif /* INFOBAR_H */ diff --git a/src/infobar.o b/src/infobar.o new file mode 100644 index 0000000000000000000000000000000000000000..3aa5a179a12f128f27fdb5ccf12a0e173a9910d4 GIT binary patch literal 8640 zcmbVR4{Te-8GmjnriUZZ$GBmQ$u?Qh8bcNZHrKPCS0%Og7-@SXz z<<+m9IO*l?zWd#G-+lk?b9>N?Zdl;)R2iyyjJ1Zxo@o8RSLP-mISd(#jYUSB@ejAK z!P05a#%LoOjh$xvz%b{Ya8QqP)Q9isKU4)NrMaPF*@R#vN89F8E0mvseF!4N* zqhcWwd%cXOy-~ivQZ=bP1|iHb#y?{GhLa@Ms=tI^LTB*d$b4?2bYfHt6StV~#zY95 z!uYNQm*e2_cr*W?IWHaOB;D2Kq)9YNw{hcf1U!yuJV<>G9@6s~nKIt+3LuoF8X*MK z6%JVz1dxXti_0v)B#e%Qd4S0>n46PuBN5Me-sJHd_QNJ2IIUSIZul?sI_C;?f~_1p zLpS7XrHB!BnlmpOJarnh`**ytOST5z@*&I(8E@=R=|P0p3}Ol2L8$y-9lii)I7iZO z7MTm4rvhX@j*xQ^s)roYmIw%YQl0Bm``o8sKMgO%Qmd6xB)68 zHbEJhFOvnOqe93j9)F+5Cb@Z5vn5?43n}*EN_zva-3(m9k1r4wq1t)#+e0-p4+|#y zGVXR(JN}8sGmgb66C0yQA~B>E5r4mM#7oy)ts-tM&YHqGb%0DYi%tHhsF6#}$U;O5 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 literal 0 HcmV?d00001 diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..7fba18e --- /dev/null +++ b/src/layout.c @@ -0,0 +1,316 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include "layout.h" +#include "config.h" +#include "client.h" +#include "util.h" + +static struct geo +layout_split(struct client *c, bool vertical) +{ + struct geo og, geo; + + geo = og = c->geo; + + if(vertical) + { + c->geo.w >>= 1; + geo.x = c->geo.x + c->geo.w; + geo.w >>= 1; + + /* Remainder */ + geo.w += (og.x + og.w) - (geo.x + geo.w); + } + else + { + c->geo.h >>= 1; + geo.y = c->geo.y + c->geo.h; + geo.h >>= 1; + + /* Remainder */ + geo.h += (og.y + og.h) - (geo.y + geo.h); + } + + client_moveresize(c, c->geo); + + return geo; +} + +static inline void +layout_split_arrange_size(struct geo g, struct client *c, Position p) +{ + if(LDIR(p)) + { + c->geo.w += g.w; + + if(p == Right) + c->geo.x = g.x; + } + else + { + c->geo.h += g.h; + + if(p == Bottom) + c->geo.y = g.y; + } + + client_moveresize(c, c->geo); +} + +static inline bool +layout_split_check_row_dir(struct client *c, struct client *g, Position p) +{ + struct geo cgeo = c->geo; + struct client *cc; + int s = 0, cs = (LDIR(p) ? g->geo.h : g->geo.w); + + SLIST_FOREACH(cc, &c->tag->clients, tnext) + if(GEO_PARENTROW(cgeo, cc->geo, RPOS(p)) + && GEO_CHECK_ROW(cc->geo, g->geo, p)) + { + s += (LDIR(p) ? cc->geo.h : cc->geo.w); + + if(s == cs) + return true; + if(s > cs) + return false; + } + + return false; +} + +/* Use ghost client properties to fix holes in tile + * + * ~ .--. ~ ~ + *_____ ~ /xx \ ~ ~ + * |>>| ~\O _ (____ ~ + * | |__.| .--'-==~ ~ + * |>>'---\ '. ~ , ~ + *__|__| '. '-.___.-'/ ~ + * '-.__ _.' ~ + * ````` ~ + */ +void +layout_split_arrange_closed(struct client *ghost) +{ + struct client *c, *cc; + struct geo g; + bool b = false; + Position p; + + + /* Search for single parent for easy resize + * Example case: + * ___________ ___________ + * | | B | -> -> | | | + * | A |_____| -> Close -> | A | B | + * | | C | -> C -> | |v v v| + * |_____|_____| -> -> |_____|_____| + */ + for(p = Right; p < Center; ++p) /* Check every direction */ + { + if((c = client_next_with_pos(ghost, p))) + if(GEO_CHECK2(ghost->geo, c->geo, p)) + { + layout_split_arrange_size(ghost->geo, c, p); + return; + } + } + + /* Check row parents for full resize + * Example case: + * ___________ ___________ + * | | B | -> -> | << B | + * | A |_____| -> Close -> |___________| + * | | C | -> A -> | << C | + * |_____|_____| -> -> |___________| + */ + for(p = Right; p < Center && !b; ++p) + { + if((c = client_next_with_pos(ghost, p)) + && layout_split_check_row_dir(c, ghost, p)) + { + g = c->geo; + SLIST_FOREACH(cc, &c->tag->clients, tnext) + if(GEO_PARENTROW(g, cc->geo, RPOS(p)) + && GEO_CHECK_ROW(cc->geo, ghost->geo, p)) + { + layout_split_arrange_size(ghost->geo, cc, p); + b = true; + } + } + } +} + +/* Integrate a client in split layout: split sc and fill c in new geo */ +void +layout_split_integrate(struct client *c, struct client *sc) +{ + struct geo g; + + /* No sc */ + if(!sc || sc == c || sc->tag != c->tag) + { + /* + * Not even a first client in list, then + * maximize the lonely client + */ + if(!(sc = SLIST_FIRST(&c->tag->clients))) + { + client_maximize(c); + return; + } + } + + g = layout_split(sc, (sc->geo.h < sc->geo.w)); + client_moveresize(c, g); +} + +/* Arrange inter-clients holes: + * ___________ ___________ + * | || | -> | | | + * | A || B | -> | A >| B | + * | || | -> | >| | + * |_____||____| -> |______|____| + * ^ void + * + * and client-screen edge holes + * ___________ ___________ + * | | || -> | | | + * | A | B || -> | A | B >| + * | | || -> | | >| + * |_____|----'| -> |_____|__v__| + * ^^^ void + */ +static inline void +layout_fix_hole(struct client *c) +{ + struct client *cr = client_next_with_pos(c, Right); + struct client *cb = client_next_with_pos(c, Bottom); + + c->geo.w += (cr ? cr->geo.x : c->screen->ugeo.w) - (c->geo.x + c->geo.w); + c->geo.h += (cb ? cb->geo.y : c->screen->ugeo.h) - (c->geo.y + c->geo.h); + + client_moveresize(c, c->geo); +} + +/* Layout rotation: Rotate 90° all client to right or left. + * Avoid if(left) condition in layout_rotate loop; use func ptr + * + * Left rotation + * ____________ ____________ + * | | B | -> | | A | + * | A |_______| -> |__|_________| + * |____| C | D | -> |_____| B | + * |____|___|___| -> |_____|______| + * + * Right rotation + * ____________ ____________ + * | | B | -> | B |_____| + * | A |_______| -> |______|_____| + * |____| C | D | -> | A | | + * |____|___|___| -> |_________|__| + * + */ + +static inline void +_pos_rotate_left(struct geo *g, struct geo ug, struct geo og) +{ + g->x = (ug.h - (og.y + og.h)); + g->y = og.x; +} + +static inline void +_pos_rotate_right(struct geo *g, struct geo ug, struct geo og) +{ + g->x = og.y; + g->y = (ug.w - (og.x + og.w)); +} + +static void +layout_rotate(struct tag *t, bool left) +{ + struct client *c; + struct geo g; + float f1 = (float)t->screen->ugeo.w / (float)t->screen->ugeo.h; + float f2 = 1 / f1; + void (*pos)(struct geo*, struct geo, struct geo) = + (left ? _pos_rotate_left : _pos_rotate_right); + + SLIST_FOREACH(c, &t->clients, tnext) + { + pos(&g, t->screen->ugeo, c->geo); + + g.x *= f1; + g.y *= f2; + g.w = c->geo.h * f1; + g.h = c->geo.w * f2; + + client_moveresize(c, g); + } + + /* Rotate sometimes do not set back perfect size.. */ + SLIST_FOREACH(c, &t->clients, tnext) + layout_fix_hole(c); +} + +void +uicb_layout_rotate_left(Uicb cmd) +{ + (void)cmd; + layout_rotate(W->screen->seltag, true); +} + +void +uicb_layout_rotate_right(Uicb cmd) +{ + (void)cmd; + layout_rotate(W->screen->seltag, false); +} + +/* + * Really simple functions, don't need static no-uicb backend + * so we avoid the use of if(vertical) .. else + * + * Vertical mirror + * ____________ ____________ + * | | B | -> | B | | + * | A |_______| -> |_______| A | + * | | C | D | -> | D | C | | + * |____|___|___| -> |___|___|____| + * + * Horizontal mirror + * ____________ ____________ + * | | B | -> | | C | D | + * | A |_______| -> | A |___|___| + * | | C | D | -> | | B | + * |____|___|___| -> |____|_______| + */ +void +uicb_layout_vmirror(Uicb cmd) +{ + (void)cmd; + struct client *c; + + SLIST_FOREACH(c, &W->screen->seltag->clients, tnext) + { + c->geo.x = W->screen->ugeo.w - (c->geo.x + c->geo.w); + client_moveresize(c, c->geo); + } +} + +void +uicb_layout_hmirror(Uicb cmd) +{ + (void)cmd; + struct client *c; + + SLIST_FOREACH(c, &W->screen->seltag->clients, tnext) + { + c->geo.y = W->screen->ugeo.h - (c->geo.y + c->geo.h); + client_moveresize(c, c->geo); + } +} diff --git a/src/layout.h b/src/layout.h new file mode 100644 index 0000000..0cdc7a7 --- /dev/null +++ b/src/layout.h @@ -0,0 +1,40 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef LAYOUT_H +#define LAYOUT_H + +#include "wmfs.h" + +/* Check lateral direction (if p is Right or Left) */ +#define LDIR(P) (P < Top) + +/* Reverse position */ +#define RPOS(P) (P & 1 ? P - 1 : P + 1) + +/* geo comparaison */ +#define GEO_CHECK2(g1, g2, p) (LDIR(p) ? ((g1).h == (g2).h) : ((g1).w == (g2).w)) +#define GEO_CHECK_ROW(g1, g2, p) \ + (LDIR(p) \ + ? ((g1).y >= (g2).y && ((g1).y + (g1).h) <= ((g2).y + (g2).h)) \ + : ((g1).x >= (g2).x && ((g1).x + (g1).w) <= ((g2).x + (g2).w))) +#define GEO_PARENTROW(g1, g2, p) \ + (LDIR(p) \ + ? (p == Left ? ((g1).x == (g2).x) : ((g1).x + (g1).w == (g2).x + (g2).w)) \ + : (p == Top ? ((g1).y == (g2).y) : ((g1).y + (g1).h == (g2).y + (g2).h))) + + +/* Debug */ +#define DGEO(G) printf(": %d %d %d %d\n", G.x, G.y, G.w, G.h) + +void layout_split_integrate(struct client *c, struct client *sc); +void layout_split_arrange_closed(struct client *ghost); +void uicb_layout_vmirror(Uicb cmd); +void uicb_layout_hmirror(Uicb cmd); +void uicb_layout_rotate_left(Uicb cmd); +void uicb_layout_rotate_right(Uicb cmd); + +#endif /* LAYOUT_H */ + diff --git a/src/layout.o b/src/layout.o new file mode 100644 index 0000000000000000000000000000000000000000..a8b683ce03923fb49c918ea4f2b89a34866f3161 GIT binary patch literal 7008 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 + * Copyright (c) 2011 Martin Duquesnoy + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parse.h" +#include "util.h" + +extern char *__progname; + +enum keyword_t { SEC_START, SEC_END, INCLUDE, WORD, EQUAL, LIST_START, LIST_END, NONE }; + +#ifdef DEBUG +static const struct +{ + const char *name; + enum keyword_t type; +} kw_t_name[] = +{ + {"SEC_START", SEC_START}, + {"SEC_END", SEC_END}, + {"INCLUDE", INCLUDE}, + {"WORD", WORD}, + {"EQUAL", EQUAL}, + {"LIST_START", LIST_START}, + {"LIST_END", LIST_END}, + {"NONE", NONE}, +}; +#endif + +struct files +{ + char *name; + struct files *parent; +}; + +struct keyword +{ + enum keyword_t type; + /* if WORD */ + int line; + struct files *file; + char *name; + struct keyword *next; +}; + +struct state +{ + bool quote; + bool comment; + char quote_char; +}; + +/* TO REMOVE (use a identifier for config and fallback XDG in api functions) */ +TAILQ_HEAD(, conf_sec) config; +static struct keyword *keywords = NULL; + +static struct keyword * +push_keyword(struct keyword *tail, enum keyword_t type, char *buf, size_t *offset, struct files *file, int line) +{ + struct keyword *kw; +#ifdef DEBUG + int i = 0; +#endif + + if(type == WORD && *offset == 0) + return tail; + + kw = xcalloc(1, sizeof(*kw)); + kw->type = type; + kw->line = line; + kw->file = file; + kw->next = NULL; + + if(*offset) + { + buf[*offset] = '\0'; + + if(!strcmp(buf, INCLUDE_CMD)) + kw->type = INCLUDE; + else + kw->name = strdup(buf); + + *offset = 0; + } + else + kw->name = NULL; + + if(tail) + tail->next = kw; + +#ifdef DEBUG + for(i = 0; kw_t_name[i].type != NONE; ++i) + if(kw_t_name[i].type == kw->type) + warnx("%s %s %s:%d\n", kw_t_name[i].name, + (kw->name) ? kw->name : "", + kw->file->name, kw->line); +#endif + + return kw; +} + +static void +syntax(struct keyword *kw, const char *fmt, ...) +{ + va_list args; + + fprintf(stderr, "%s:", __progname); + + if(kw && kw->file && kw->file->name) + fprintf(stderr, "%s:%d", kw->file->name, kw->line); + + if(kw && kw->name) + fprintf(stderr, ", near '%s'", kw->name); + fprintf(stderr, ": "); + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + + fprintf(stderr, "\n"); +} + + +#define PUSH_KEYWORD(type) tail = push_keyword(tail, type, bufname, &j, file, line) +static struct keyword * +parse_keywords(const char *filename) +{ + int fd; + struct stat st; + char *buf; + struct keyword *head = NULL; + struct keyword *tail = NULL; + struct files *file; + enum keyword_t type; /* keyword type to push */ + struct state s = { false, false, '\0'}; + char *bufname; + char path[PATH_MAX]; + size_t i, j; + int line; + bool error = false; + + + if((fd = open(filename, O_RDONLY)) == -1 || stat(filename, &st) == -1) + { + warn("%s", filename); + return NULL; + } + + if(!st.st_size) + { + warnx("%s: empty file", filename); + close(fd); + return NULL; + } + + if(!realpath(filename, path)) + { + warn("%s", filename); + close(fd); + return NULL; + } + + buf = xmalloc(1, st.st_size + 1); + + if(read(fd, buf, st.st_size) == -1) + { + warn("%s", filename); + free(buf); + close(fd); + return NULL; + } + + buf[st.st_size] = '\0'; + + file = xcalloc(1, sizeof(*file)); + bufname = xcalloc(BUFSIZ, sizeof(*bufname)); + file->name = strdup(path); + file->parent = NULL; + + for(i = j = 0, line = 1; i < (size_t)st.st_size; ++i) + { + if(!head && tail) + head = tail; + + if(buf[i] == '\n' && s.comment) + { + line++; + s.comment = false; + continue; + } + + if(buf[i] == '#' && !s.quote) + { + s.comment = true; + continue; + } + + if(s.comment) + continue; + + /* end of quotted string */ + if(s.quote && buf[i] == s.quote_char) + { + PUSH_KEYWORD(WORD); + s.quote = false; + continue; + } + + if(!s.quote) + { + if((buf[i] == '"' || buf[i] == '\'')) + { + PUSH_KEYWORD(WORD); + /* begin quotted string */ + s.quote_char = buf[i]; + s.quote = true; + continue; + } + + if(buf[i] == '[') + { + PUSH_KEYWORD(WORD); + + if(buf[i + 1] == '/') + { + i += 2; + type = SEC_END; + } + else + { + ++i; + type = SEC_START; + } + + /* get section name */ + while(buf[i] != ']') + { + if(i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1)) + { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = true; + break; + } + bufname[j++] = buf[i++]; + } + PUSH_KEYWORD(type); + continue; + } + + if(buf[i] == '{') + { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_START); + continue; + } + + if(buf[i] == '}') + { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(LIST_END); + continue; + } + + if(buf[i] == ',') + { + PUSH_KEYWORD(WORD); + continue; + } + + if(buf[i] == '=') + { + PUSH_KEYWORD(WORD); + PUSH_KEYWORD(EQUAL); + continue; + } + + if(strchr("\t\n ", buf[i])) + { + PUSH_KEYWORD(WORD); + + if(buf[i] == '\n') + ++line; + + continue; + } + } + + if(j >= (BUFSIZ - 1)) + { + bufname[j] = '\0'; + syntax(NULL, "word too long in %s:%d near '%s'", + file->name, line, bufname); + error = true; + break; + } + + bufname[j++] = buf[i]; + } + + free(buf); + free(bufname); + close(fd); + warnx("%s read", file->name); + + return (error ? NULL: head); +} + +/* + * return NULL on failure and head->next if + * no config found (of file doesn't exist) + * NOTE to devs: head->name is the file to include + */ +static struct keyword * +include(struct keyword *head) +{ + struct keyword *kw; + struct keyword *tail; + struct files *file; + struct passwd *user; + char *filename = NULL; + char *base = NULL; + + head = head->next; + + if(!head || head->type != WORD) + { + syntax(head, "missing filename to include"); + return NULL; + } + + /* replace ~ by user directory */ + if(head->name && head->name[0] == '~') + { + if((user = getpwuid(getuid())) && user->pw_dir) + xasprintf(&filename, "%s%s", user->pw_dir, head->name + 1); + else if(getenv("HOME")) + xasprintf(&filename, "%s%s", getenv("HOME"), head->name + 1); + else /* to warning ? */ + filename = head->name; + } + + /* relative path from parent file */ + else if(head->name && head->name[0] != '/') + { + base = strdup(head->file->name); + xasprintf(&filename, "%s/%s", dirname(base), head->name); + free(base); + } + else + filename = head->name; + + if(!(kw = parse_keywords(filename))) + { + warnx("no config found in include file %s", head->name); + + if(filename != head->name) + free(filename); + + return NULL; + } + + kw->file->parent = head->file; + + /* detect circular include */ + for(file = kw->file->parent; file != NULL; file = file->parent) + if(!strcmp(file->name, kw->file->name)) + { + syntax(kw, "circular include of %s", kw->file->name); + + if(filename != head->name) + free(filename); + + return NULL; + } + + if(filename != head->name) + free(filename); + + head = head->next; + + if(kw) + { + for(tail = kw; tail->next; tail = tail->next); + tail->next = head; + } + + return kw; +} + +static void * +free_opt(struct conf_opt *o) +{ + free(o); + return NULL; +} + +static struct conf_opt * +get_option(struct keyword **head) +{ + struct conf_opt *o; + size_t j = 0; + struct keyword *kw = *head; + + o = xcalloc(1, sizeof(*o)); + o->name = kw->name; + o->used = false; + o->line = kw->line; + o->filename = kw->file->name; + + kw = kw->next; + + if(kw->type != EQUAL) + { + syntax(kw, "missing '=' here"); + return free_opt(o); + } + + if(!(kw = kw->next)) + { + syntax(kw, "missing value"); + return free_opt(o); + } + + switch(kw->type) + { + case INCLUDE: + if(!(kw = include(kw))) + return free_opt(o); + break; + + case WORD: + o->val[0] = kw->name; + o->val[1] = NULL; + kw = kw->next; + break; + + case LIST_START: + kw = kw->next; + + while(kw && kw->type != LIST_END) + { + switch(kw->type) + { + case WORD: + if(j >= (PARSE_MAX_LIST - 1)) + { + syntax(kw, "too much values in list"); + return free_opt(o); + } + + o->val[j++] = kw->name; + kw = kw->next; + break; + + case INCLUDE: + if(!(kw = include(kw))) + return free_opt(o); + break; + + default: + syntax(kw, "declaration into a list"); + return free_opt(o); + break; + } + } + + if(!kw) + { + syntax(kw, "list unclosed"); + return free_opt(o); + } + + kw = kw->next; + break; + + default: + syntax(kw, "missing value"); + return free_opt(o); + break; + } + + *head = kw; + return o; +} + +static void * +free_sec(struct conf_sec *sec) +{ + struct conf_opt *o; + struct conf_sec *s; + + if(sec) + { + while(!SLIST_EMPTY(&sec->optlist)) + { + o = SLIST_FIRST(&sec->optlist); + SLIST_REMOVE_HEAD(&sec->optlist, entry); + free_opt(o); + } + + while(!TAILQ_EMPTY(&sec->sub)) + { + s = TAILQ_FIRST(&sec->sub); + TAILQ_REMOVE(&sec->sub, s, entry); + free_sec(s); + } + + free(sec); + } + + return NULL; +} + +static struct conf_sec * +get_section(struct keyword **head) +{ + struct conf_sec *s; + struct conf_opt *o; + struct conf_sec *sub; + struct keyword *kw = *head; + + s = xcalloc(1, sizeof(*s)); + s->name = kw->name; + + TAILQ_INIT(&s->sub); + SLIST_INIT(&s->optlist); + + kw = kw->next; + + while(kw && kw->type != SEC_END) + { + switch(kw->type) + { + case INCLUDE: + if(!(kw = include(kw))) + return free_sec(s); + break; + + case SEC_START: + if(!(sub = get_section(&kw))) + return free_sec(s); + TAILQ_INSERT_TAIL(&s->sub, sub, entry); + ++s->nsub; + break; + + case WORD: + if(!(o = get_option(&kw))) + return free_sec(s); + SLIST_INSERT_HEAD(&s->optlist, o, entry); + ++s->nopt; + break; + + default: + syntax(kw, "syntax error"); + return free_sec(s); + break; + } + } + + if(!kw || strcmp(kw->name, s->name)) + { + syntax(kw, "missing end section %s", s->name); + return free_sec(s); + } + + kw = kw->next; + *head = kw; + + return s; +} + +int +free_conf(void) +{ + struct conf_sec *s; + struct keyword *kw, *nkw; + struct files **f = NULL; + int i, nf = 0; + + while(!TAILQ_EMPTY(&config)) + { + s = TAILQ_FIRST(&config); + TAILQ_REMOVE(&config, s, entry); + free_sec(s); + } + + kw = keywords; + + while(kw) + { + nkw = kw->next; + + free(kw->name); + + for(i = 0; i < nf; ++i) + if(f[i] == kw->file) + { + if(!(f = realloc(f, sizeof(*f) * (++i)))) + err(EXIT_FAILURE, "realloc"); + + f[i - 1] = kw->file; + } + + kw = nkw; + } + + if(nf > 0) + { + for(i = 0; i < nf; ++i) + { + free(f[i]->name); + free(f[i]); + } + free(f); + } + + return -1; +} + +int +get_conf(const char *filename) +{ + struct conf_sec *s; + struct keyword *head, *kw; + + head = kw = parse_keywords(filename); + + if(!head) + return -1; /* TODO ERREUR */ + + keywords = head; + + TAILQ_INIT(&config); + + while(kw) + { + switch(kw->type) + { + case INCLUDE: + if(!(kw = include(kw))) + return free_conf(); + break; + + case SEC_START: + if(!(s = get_section(&kw))) + return free_conf(); + TAILQ_INSERT_TAIL(&config, s, entry); + break; + + default: + syntax(kw, "out of any section"); + return free_conf(); + break; + } + } + + return 0; +} + diff --git a/src/parse.h b/src/parse.h new file mode 100644 index 0000000..868f5b1 --- /dev/null +++ b/src/parse.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010 Philippe Pepiot + * Copyright (c) 2011 Martin Duquesnoy + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PARSE_H +#define PARSE_H + +#include "wmfs.h" + +#define INCLUDE_CMD "@include" +#define PARSE_MAX_LIST 32 + +struct conf_opt +{ + char *name; + char *val[PARSE_MAX_LIST]; + size_t nval; + bool used; + int line; + char *filename; + SLIST_ENTRY(conf_opt) entry; +}; + +struct conf_sec +{ + char *name; + SLIST_HEAD(, conf_opt) optlist; + TAILQ_HEAD(cshead, conf_sec) sub; + size_t nopt; + size_t nsub; + TAILQ_ENTRY(conf_sec) entry; +}; + +struct opt_type +{ + long int num; + float fnum; + bool boolean; + char *str; +}; + +/* + * Create config from file + * return -1 on failure + */ +int get_conf(const char *); + +/* + * Print unused option name from section s (and subsections). + * If s == NULL print unused option name for all config struct. + */ +void print_unused(struct conf_sec *s); + +/* + * Free the config struct. + * WARNING: This make all string + * returned by fetch_(opt|section)(_first) unusable. + */ +int free_conf(void); + +/* + * Get all subsection matching the given name on the given + * section. + * If section == NULL, return subsections from root section. + * Return a NULL terminated array. + * Subsections are returned in order as they are in config file + * WARNING : This MUST be free() after use. + */ +struct conf_sec **fetch_section(struct conf_sec *, char *); + +/* + * Get first subsection matching the given name + * on the given section. (first found on the file) + */ +struct conf_sec *fetch_section_first(struct conf_sec *, char *); + +/* + * Count member of a conf_sec ** + */ +size_t fetch_section_count(struct conf_sec **); + +/* + * Return all options matching the given name on the given subsection. + * If none match or section == NULL return opt_type build with the + * given default param. + * WARNING: This MUST be free() after use. + * WARNING: The string member is directly taken from the config struct. + * WARNING: Returned in reverse order as they are in config file. + * (I think the last option MUST overwrite all others) + */ +struct opt_type fetch_opt_first(struct conf_sec *, char *, char *); + +/* + * Get first (last in config file) option matching the given name + * on the given section. + * WARNING: The string member is directly taken from the config struct. + */ +struct opt_type *fetch_opt(struct conf_sec *, char *, char *); + +/* + * Count member of a opt_type * + */ +size_t fetch_opt_count(struct opt_type *); + +#endif /* PARSE_H */ diff --git a/src/parse.o b/src/parse.o new file mode 100644 index 0000000000000000000000000000000000000000..4bc143adda9960e39d037aa6df8d0f764308797d GIT binary patch literal 13480 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 mG~t*a-v}o=%Jik`%@=vY`Z<3W;vdwBXj-v5 + * Copyright (c) 2011 Martin Duquesnoy + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include +#include +#include + +#include "wmfs.h" +#include "parse.h" +#include "util.h" + +extern TAILQ_HEAD(, conf_sec) config; + +static const struct opt_type opt_type_null = { 0, 0, false, NULL }; + +static struct opt_type +string_to_opt(char *s) +{ + struct opt_type ret = opt_type_null; + + if(!s || !strlen(s)) + return ret; + + ret.num = strtol(s, (char**)NULL, 10); + ret.fnum = strtod(s, NULL); + + ret.boolean = (!strcmp(s, "true") + || !strcmp(s, "true") + || !strcmp(s, "TRUE") + || !strcmp(s, "1")); + + ret.str = s; + + return ret; +} + + +void +print_unused(struct conf_sec *sec) +{ + struct conf_sec *s; + struct conf_opt *o; + + if(!sec) + { + TAILQ_FOREACH(s, &config, entry) + print_unused(s); + return; + } + + SLIST_FOREACH(o, &sec->optlist, entry) + if(!o->used) + warnx("%s:%d, unused param %s", o->filename, o->line, o->name); + + TAILQ_FOREACH(s, &sec->sub, entry) + if(!TAILQ_EMPTY(&s->sub)) + print_unused(s); +} + +struct conf_sec ** +fetch_section(struct conf_sec *s, char *name) +{ + struct conf_sec **ret; + struct conf_sec *sec; + size_t i = 0; + + if(!name) + return NULL; + + if(!s) + { + ret = xcalloc(2, sizeof(struct conf_sec *)); + + TAILQ_FOREACH(sec, &config, entry) + if(!strcmp(sec->name, name)) + { + ret[0] = sec; + ret[1] = NULL; + break; + } + } + else + { + ret = xcalloc(s->nsub + 1, sizeof(struct conf_sec *)); + + TAILQ_FOREACH(sec, &s->sub, entry) + if(!strcmp(sec->name, name) && i < s->nsub) + ret[i++] = sec; + + ret[i] = NULL; + } + + return ret; +} + +struct conf_sec * +fetch_section_first(struct conf_sec *s, char *name) +{ + struct conf_sec *sec, *ret = NULL; + TAILQ_HEAD(cshead, conf_sec) *head = + (s + ? (struct cshead*)&s->sub + : (struct cshead*)&config); + + if(!name) + return NULL; + + TAILQ_FOREACH(sec, head, entry) + if(sec->name && !strcmp(sec->name, name)) + { + ret = sec; + break; + } + + return ret; +} + +size_t +fetch_section_count(struct conf_sec **s) +{ + size_t ret = 0; + + while(s[ret]) + ++ret; + + return ret; +} + +struct opt_type * +fetch_opt(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + struct opt_type *ret; + size_t i = 0; + + if(!name) + return NULL; + + ret = xcalloc(10, sizeof(struct opt_type)); + + if(s) + { + SLIST_FOREACH(o, &s->optlist, entry) + if(!strcmp(o->name, name)) + { + while(o->val[i]) + { + o->used = true; + ret[i] = string_to_opt(o->val[i]); + ++i; + } + + ret[i] = opt_type_null; + + return ret; + } + } + + ret[0] = string_to_opt(dfl); + ret[1] = opt_type_null; + + return ret; +} + +struct opt_type +fetch_opt_first(struct conf_sec *s, char *dfl, char *name) +{ + struct conf_opt *o; + + if(!name) + return opt_type_null; + else if(s) + { + SLIST_FOREACH(o, &s->optlist, entry) + if(!strcmp(o->name, name)) + { + o->used = true; + + return string_to_opt(o->val[0]); + } + } + + return string_to_opt(dfl); +} + +size_t +fetch_opt_count(struct opt_type *o) +{ + size_t ret = 0; + + while(o[ret].str) + ++ret; + + return ret; +} + + diff --git a/src/parse_api.o b/src/parse_api.o new file mode 100644 index 0000000000000000000000000000000000000000..15af36ff29dd5fdf47a7617bd422d4d61d940102 GIT binary patch literal 5312 zcmbVPZERat89uiOG-GX@Oasb?s0XPOzRZfy)PgAPh}XkZZ`p>01gkvHb=I)Nw)`Q| zK_DebYv-z3e~d3pi2dNlBsMKtw=SKuHd&`RDgq)vlYf8|A;xT}Na+wFpl;!L&d2rj zt=&p{m3z-Q&--!S^SxL) zzSQ8LOuExya4#WuKVCbh>lSaT=Hys(8lkUZa~Cqkd28e8*4xsG$y zt&fu8T)TUvTW%BbS5 zZwyIiSyunx^NAT(v263{yEN0;r~R6>>RH^k5}NrmLH}m3mROb5#ENuY#QmxYCA=C$bIi>FQf`twuQqUCj)A5*V5(Yb!|KhmF70i zPkB|o0?TkEMptafwr4j-1(yN>a&R4wsi{Mn1x#3I$bbc2pQmnf232+XwdSw& z>Fe#M4ei3FKN#GQwc){an1&~qNcRvRw|Cj@s;a{m^h1zgdNDRHDZ|jZIO?zO5wE_h z34}YgqOD_o{C}JVYWz{w_=OLU1Cr-=>WNp=jGw&#vkry1*6jc(Mw(5`-lV!$Piw0F zOpTw_ETkk3mEq++tMd>UzT?H~uODYK!8M3O8a3tlBSX(!kOo_5t1|9Fu3AQNRlSe2>Ye%0fjq?y0Fl}O zW8r|YfQT@*akTXRswvC5I<*p-{h(q@ZK!CQsLp+g;3-x#PVJ8L%j$jo(#0Qp_yIfv z%>kHPaO9~k>5sxWS|z>ALQo$M#<%)=S&c7NVlQd)>XPu=QeITR+o5kS{VO!UuYy9C z6smGQJRf^$xDq=Xnw@|_-%gOM#9nL(&c|P@#36Q92eB9YVlODMU3AZS4O=aV&`*}9 zuQ(8zkDZl>WF>ZH(3wx1nf{woNnD1{8;I>XMug-TLh?hzV;|Zj!f_9t{gv4B=tIa0 zF4HV_Y5QL2{4h7w#5!Vv6XOXaV=fFORXw$+NuciIvPN^FS=IavTm9p9t#1?Z)#^Ya zng?vJsx>TyxlOkj*6Y*esxZ@eUWC=g)9>H|VFg-+yViV46)pn+sUJg1H{H?E(TF{7 z*M4-(#c}WSzP{IMQ0Ke`MeoJ#;+Ji_o+tJ`6k~x|jMYb{`lFgMek%RXnK3^=Q(vONr z@hEglsfRH;HlateE@Z}2WHP~-~42ZWkEys>| z;J5Hx#wAr#!1&=KuA5CO=f5=gh}l}z46x4{JZx%2-2q?^8hpWo!q&1h_>u{ox(mSg z>s0xJk)-Y%#!ui;{g0rJ4q)C1uXMqG+68~S3(ikjC;R)l;G$Sae*tZ%m`SGcW0ADT z7sj%sr0!25oy(4nJtDrBDr6_cXr`1tnk;70rLkOAOr}%g8B_1?abupSfN-7 zVCh^rYq4CuM8&b}Bgs-OX;$NzEWuK4Tn|T#0kGQ)a;=SVRyvl?B(vr5alwDZ#BF?m zZpLGKB-geF@zd_F!ZT!onAVTrxr%m8`o5_P;{5l};wKFrwEr8QJb#Z^`H-oXpq;Pz zaKHRkQ+Gl8S%Xu}FMqGO2SNLf`*6SfJLbw#{zE=L?=V-E`|_VjOAi{H^6WYGToXS0 z!#;e*hx_+^s*C++y2#f8^0w3bsf+yOfIRa|`ga%kznUTl#xtz6grL3&p+b27Nfx(# zgZ+qPaoZn$u3-(%Xd`ne z?047ltiD};29r+v?=YohQ2}NYBf3l literal 0 HcmV?d00001 diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 0000000..8724dcd --- /dev/null +++ b/src/screen.c @@ -0,0 +1,114 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifdef HAVE_XINERAMA +#include +#endif /* HAVE_XINERAMA */ + +#include "screen.h" +#include "util.h" +#include "tag.h" +#include "infobar.h" +#include "client.h" + +static struct screen* +screen_new(struct geo *g, int id) +{ + struct screen *s = (struct screen*)xcalloc(1, sizeof(struct screen)); + + s->geo = s->ugeo = *g; + s->seltag = NULL; + s->id = id; + + TAILQ_INIT(&s->tags); + SLIST_INIT(&s->infobars); + + SLIST_INSERT_HEAD(&W->h.screen, s, next); + + /* Set as selected screen */ + W->screen = s; + + return s; +} + +void +screen_init(void) +{ + struct geo g; + + SLIST_INIT(&W->h.screen); + +#ifdef HAVE_XINERAMA + XineramaScreenInfo *xsi; + int i, n = 0; + + if(XineramaIsActive(W->dpy)) + { + xsi = XineramaQueryScreens(W->dpy, &n); + + for(i = 0; i < n; ++i) + { + g.x = xsi[i].x_org; + g.y = xsi[i].y_org; + g.w = xsi[i].width; + g.h = xsi[i].height; + + screen_new(&g, i); + } + + XFree(xsi); + } + else +#endif /* HAVE_XINERAMA */ + { + g.x = g.y = 0; + g.w = DisplayWidth(W->dpy, W->xscreen); + g.h = DisplayHeight(W->dpy, W->xscreen); + + screen_new(&g, 0); + } +} + +/* + * Update selected screen with mouse location + */ +struct screen* +screen_update_sel(void) +{ +#ifdef HAVE_XINERAMA + if(XineramaIsActive(W->dpy)) + { + struct screen *s; + Window w; + int d, x, y; + + XQueryPointer(W->dpy, W->root, &w, &w, &x, &y, &d, &d, (unsigned int *)&d); + + SLIST_FOREACH(s, &W->h.screen, next) + if(INAREA(x, y, s->geo)) + break; + + return (W->screen = s); + } +#endif /* HAVE_XINERAMA */ + + return W->screen; +} + +void +screen_free(void) +{ + struct screen *s; + + while(!SLIST_EMPTY(&W->h.screen)) + { + s = SLIST_FIRST(&W->h.screen); + SLIST_REMOVE_HEAD(&W->h.screen, next); + + infobar_free(s); + tag_free(s); + free(s); + } +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..7d86f02 --- /dev/null +++ b/src/screen.h @@ -0,0 +1,27 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef SCREEN_H +#define SCREEN_H + +#include "wmfs.h" + +static inline struct screen* +screen_gb_id(int id) +{ + struct screen *s; + + SLIST_FOREACH(s, &W->h.screen, next) + if(s->id == id) + return s; + + return SLIST_FIRST(&W->h.screen); +} + +void screen_init(void); +struct screen* screen_update_sel(void); +void screen_free(void); + +#endif /* SCREEN_H */ diff --git a/src/screen.o b/src/screen.o new file mode 100644 index 0000000000000000000000000000000000000000..ce751674012e73ac807d5c14a8a5a25e16272df9 GIT binary patch literal 3608 zcmbVN-)me&6rP*irm;R`_Q61`Vzu)+3LV? z*)HNxgLy{@*Kc5FUi}?pST;v0Qbh{XHzV!Eg32_@t z@5B^W#ytTr+964nPm)!}Ex@4^SSJkdtA%rWVg8M_ z3wEiuU>AD}iB(!iK+PSLNqTK->cXvlq%5&Q8F~ssw2iXvHh{a^Vt&w+6Z}jEFe}Xd z1(+2ozk&C_6h@jSD!k>?iupMW0tiR?n*je3W5VrKLF|tr^M!4QPi;o(F4!oyKqm}w zi$dZmRJ>v}+Q-x4`!;XfyYk4m`oR1| z*zDbY65KaXu_9UEi;9OxQobsKq)*CUp}Vt7vEfBH^dK}8m^g~lX&(*)^ok&7|doS{ZX!6XCf(264Tll z(uF4m;W~M=sc&=v&;T#{91=i8Ec_{n)P3;@;0=>oN&IJsfMP9)3Io|0V?g zGz7<*sdxUl5S(P))QN1$b(~B*Mf}M~I+OOu+hi!6aiEZrXR@6sFMY|W0sT40ojE6z zvPvi8TqZ+3a1NPHB_}4PYKP@6LTx%DS!aR_3B|stbjEXBr8tf{>CE`lSki^Ug@u=V z*Pn_BwH*6PoBEHbgDg22miJP){~ { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include /* IconicState / NormalState */ + +#include "tag.h" +#include "util.h" +#include "infobar.h" +#include "client.h" +#include "config.h" +#include "barwin.h" +#include "ewmh.h" +#include "layout.h" + +struct tag* +tag_new(struct screen *s, char *name) +{ + struct tag *t; + XSetWindowAttributes at = + { + .background_pixel = THEME_DEFAULT->frame_bg, + .override_redirect = true, + .background_pixmap = ParentRelative, + .event_mask = BARWIN_MASK + }; + + t = xcalloc(1, sizeof(struct tag)); + + t->screen = s; + t->name = xstrdup(name); + t->flags = 0; + t->sel = NULL; + + /* Frame window */ + t->frame = XCreateWindow(W->dpy, W->root, + s->ugeo.x, s->ugeo.y, + s->ugeo.w, s->ugeo.h, + 0, CopyFromParent, + InputOutput, + CopyFromParent, + (CWOverrideRedirect | CWBackPixmap + | CWBackPixel | CWEventMask), + &at); + + SLIST_INIT(&t->clients); + + TAILQ_INSERT_TAIL(&s->tags, t, next); + + return t; +} + +void +tag_screen(struct screen *s, struct tag *t) +{ + struct client *c; + + /* Unmap previous tag's frame */ + WIN_STATE(s->seltag->frame, Unmap); + SLIST_FOREACH(c, &s->seltag->clients, tnext) + ewmh_set_wm_state(c->win, IconicState); + + /* + * Map selected tag's frame, only if there is + * clients in t + */ + if(!SLIST_EMPTY(&t->clients)) + { + WIN_STATE(t->frame, Map); + SLIST_FOREACH(c, &t->clients, tnext) + ewmh_set_wm_state(c->win, NormalState); + + client_focus(t->sel); + } + + s->seltag = t; + + infobar_elem_screen_update(s, ElemTag); +} + +/* Set t to NULL to untag c from c->tag */ +void +tag_client(struct tag *t, struct client *c) +{ + /* Remove client from its previous tag */ + if(c->tag) + { + if(c->tag == t) + return; + + layout_split_arrange_closed(c); + + SLIST_REMOVE(&c->tag->clients, c, client, tnext); + + if(c->tag->sel == c || W->client == c) + client_focus(client_next(c)); + } + + /* + * Case of client removing: umap frame if empty + */ + if(!t) + { + /* Unmap frame if tag is now empty */ + if(SLIST_EMPTY(&c->tag->clients)) + WIN_STATE(c->tag->frame, Unmap); + + return; + } + + /* Reparent client win in frame win */ + XReparentWindow(W->dpy, c->win, t->frame, 0, 0); + + /* Map frame if tag was empty */ + if(SLIST_EMPTY(&t->clients)) + WIN_STATE(t->frame, Map); + + c->tag = t; + + layout_split_integrate(c, t->sel); + + /* Insert in new tag list */ + SLIST_INSERT_HEAD(&t->clients, c, tnext); +} + +void +uicb_tag_set(Uicb cmd) +{ + int i = 0, n = ATOI(cmd); + struct tag *t; + + TAILQ_FOREACH(t, &W->screen->tags, next) + if(++i == n) + { + tag_screen(W->screen, t); + return; + } +} + +void +uicb_tag_set_with_name(Uicb cmd) +{ + struct tag *t; + + TAILQ_FOREACH(t, &W->screen->tags, next) + if(!strcmp(cmd, t->name)) + { + tag_screen(W->screen, t); + return; + } +} + +void +uicb_tag_next(Uicb cmd) +{ + (void)cmd; + struct tag *t; + + if((t = TAILQ_NEXT(W->screen->seltag, next))) + tag_screen(W->screen, t); + else if( /* CIRCULAR OPTION */ 1) + tag_screen(W->screen, TAILQ_FIRST(&W->screen->tags)); +} + +void +uicb_tag_prev(Uicb cmd) +{ + (void)cmd; + struct tag *t; + + if((t = TAILQ_PREV(W->screen->seltag, tsub, next))) + tag_screen(W->screen, t); + else if( /* CIRCULAR OPTION */ 1) + tag_screen(W->screen, TAILQ_LAST(&W->screen->tags, tsub)); +} + +static void +tag_remove(struct tag *t) +{ + free(t->name); + + XDestroyWindow(W->dpy, t->frame); + + free(t); +} + +void +tag_free(struct screen *s) +{ + struct tag *t; + + TAILQ_FOREACH(t, &s->tags, next) + { + TAILQ_REMOVE(&s->tags, t, next); + tag_remove(t); + } +} diff --git a/src/tag.h b/src/tag.h new file mode 100644 index 0000000..39c9fe5 --- /dev/null +++ b/src/tag.h @@ -0,0 +1,38 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef TAG_H +#define TAG_H + +#include "wmfs.h" + +struct tag *tag_new(struct screen *s, char *name); +void tag_screen(struct screen *s, struct tag *t); +void tag_client(struct tag *t, struct client *c); +void tag_free(struct screen *s); +void uicb_tag_set(Uicb cmd); +void uicb_tag_set_with_name(Uicb cmd); +void uicb_tag_next(Uicb cmd); +void uicb_tag_prev(Uicb cmd); + +/* + * Update frames size with screen usable geo + */ +static inline void +tag_update_frame_geo(struct screen *s) +{ + struct tag *t; + + TAILQ_FOREACH(t, &s->tags, next) + XMoveResizeWindow(W->dpy, + t->frame, + s->ugeo.x, + s->ugeo.y, + s->ugeo.w, + s->ugeo.h); +} + + +#endif /* TAG_H */ diff --git a/src/tag.o b/src/tag.o new file mode 100644 index 0000000000000000000000000000000000000000..c1fffc818d05c0d71a26dcf6016aedef0a80e0f1 GIT binary patch literal 6504 zcmbVQYiwLc6&^p@IHbkf5+KmhY^ecD7-JraT&X12)Eke{8r)FR{n2*4-dx9qePti9 zgRpE9J8*RyDMC$E36Sy&!LRxcBCSerX~goU0*NY$5JDhYv=%|yR*~wKD&~AMXLiRo zdpA-?@}0TgeCIJ|&YU~m{4{_hmeU$qH+()?Y(Y^9dR@dP)#&Q3y z&tPW4A5X;m@yFxd#EPuOHscrX6V=$CLGjK#sk~jEq|RH9(^GmE9O`P2lwPbR{n)nj zr)tvcl>Sm(N^K?FYm8* z_rYX!v`^`vJ^~LV4YUCoM6or}Uq@V}H`>?F-c-oDqfycX7y6-w$0~xbAbp2?nIPT> z^;p#eVdKNn>rk&RDkcOwUQv8cYToLVI-yU!Iy=~Va1EMu{+zi!B)z3==~e2@B|?I< zI^;0Nt>o|lp5%0+)M!8+l- zGY9t8Ylws&BPU3l#eS$HtS!KFONR)DGk;@et~E62ec$bf+6@)D_Kqy}D0P>~3ph?T zDAe8vEqrOa)(h~=l_3gfAf8dg)2rO@CvIGZslWUi0{+;*-nut7fU)$K9U>tk8=ieq z8Cj~?k&!AgS9N44)dp7od6Ql|IY5@9WLdQ~`-we0Bv)^eZrX)6lnJbZmP1Q<_>aBo zcM!JqFLKj563n1|@F(hubUC| zDY!&`6gOR)r2nuE$;kxuw8f7_y;nxOP4;MtkYoLF^*zW?qV}#IQo?3UaDgZ-?GujV zzNS=pvDY<~!tc?MU!q-5k~&%S3vXQ`nksMmIb9q1BE28cl|VOZWicI8cg{9@Mvfr9Fti z15!h({ZJaptYuhF50Z^S9ysjCn`f z@Wf!*T`Y^ifbeaxp$HFbt)^cDI8-{B>wGeg*<(X&U z=DuwBtViWG;rM`{1O?kV@mt|JBDTVzuND4I2!1#O|5gb8;}HDG5PT)X|BVp-pF;3G zA$rC`@Xv?f9}Us}sSsS0is>(wQzr(~f}Y8oyC9wri|JG*lTV99INu zOLO^>dt9)I9O_l#m|I8{$s(KIB{!cdyC;gUR+&%FB%z*GAyB*WnO#_NVZJ<<%%RxP zY&u(L&MGqv`9je>r2;ogaZ`h$o6Vncg?dknx+I=I&CbwPiT~n34duFKH>fQ7%! z_$~f$=?CA||0v@@{&y_C9p|S*_+^W4$JqH)|wD2p82jg=y1b6s56y#?ahux^9e4DikU?pgph_Yu0{sAlvG@qY9N@D0Xs|3Pc;IgX0~+}yJ+25@tK z)LK6H6Ke6t>D*A{@aQ>?znKW$g|HmRbD1(UaV;QcCb&GAS8+Fuu=Ecqe=o_xKJv_N z#{MQIt;S()AjqizMa?~r1Z~lR+yUYQeTSHlpT>*Tg7WyKDd;=s+iZWDmBVl3it!&c z84dT%R`wJ44V2om0fT{8ag;d0J%L=EH+q97%{~Y1KNcc?5k;C>4;zF$R0ZY#58<3P Ae*gdg literal 0 HcmV?d00001 diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..f66b71a --- /dev/null +++ b/src/util.c @@ -0,0 +1,128 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#define _GNU_SOURCE /* vasprintf() */ + +#include +#include + +#include "util.h" + +/** malloc with error support and size_t overflow protection + * \param nmemb number of objects + * \param size size of single object + * \return non null void pointer + */ +void* +xmalloc(size_t nmemb, size_t size) +{ + void *ret; + + if(SIZE_MAX / nmemb < size) + err(EXIT_FAILURE, "xmalloc(%zu, %zu), " + "size_t overflow detected", nmemb, size); + + if((ret = malloc(nmemb * size)) == NULL) + err(EXIT_FAILURE, "malloc(%zu)", nmemb * size); + + return ret; +} + +/** calloc with error support + * \param nmemb Number of objects + * \param size size of single object + * \return non null void pointer +*/ +void* +xcalloc(size_t nmemb, size_t size) +{ + void *ret; + + if((ret = calloc(nmemb, size)) == NULL) + err(EXIT_FAILURE, "calloc(%zu * %zu)", nmemb, size); + + return ret; +} + +/** asprintf wrapper + * \param strp target string + * \param fmt format + * \return non zero integer + */ +int +xasprintf(char **strp, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = vasprintf(strp, fmt, args); + va_end(args); + + if (ret == -1) + err(EXIT_FAILURE, "asprintf(%s)", fmt); + + return ret; +} + +/** strdup with error support + * \param str char pointer + * \retun non null void pointer + */ +char * +xstrdup(const char *str) +{ + char *ret; + + if (str == NULL || (ret = strdup(str)) == NULL) + err(EXIT_FAILURE, "strdup(%s)", str); + + return ret; +} + +/** Execute a system command + * \param cmd Command + * \return child pid +*/ +pid_t +spawn(const char *format, ...) +{ + char *sh = NULL; + char cmd[512]; + va_list ap; + pid_t pid; + size_t len; + + va_start(ap, format); + len = vsnprintf(cmd, sizeof(cmd), format, ap); + va_end(ap); + + if (len >= sizeof(cmd)) + { + warnx("command too long (> 512 bytes)"); + return -1; + } + + if(!(sh = getenv("SHELL"))) + sh = "/bin/sh"; + + if((pid = fork()) == 0) + { + setsid(); + if (execl(sh, sh, "-c", cmd, (char*)NULL) == -1) + warn("execl(sh -c %s)", cmd); + exit(EXIT_FAILURE); + } + else if (pid == -1) + warn("fork"); + + return pid; +} + +void +uicb_spawn(Uicb cmd) +{ + spawn("%s", cmd); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..9359bd7 --- /dev/null +++ b/src/util.h @@ -0,0 +1,63 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef UTIL_H +#define UTIL_H + +#include "wmfs.h" + +/* Todo FREE_LIST(type, head, function_remove) */ +#define FREE_LIST(type, head) \ + do { \ + struct type *Z; \ + while(!SLIST_EMPTY(&head)) { \ + Z = SLIST_FIRST(&head); \ + SLIST_REMOVE_HEAD(&head, next); \ + free(Z); /* function_remove(t)*/ \ + } \ + } while(/* CONSTCOND */ 0); + +/* t is Map or Unmap */ +#define WIN_STATE(w, t) do { \ + X##t##Subwindows(W->dpy, w); \ + X##t##Window(W->dpy, w); \ +} while( /* CONSTCOND */ 0); + + +#define ATOM(a) XInternAtom(W->dpy, (a), False) +#define LEN(x) (sizeof(x) / sizeof(*x)) +#define FLAGINT(i) (1 << i) +#define ATOI(s) strtol(s, NULL, 10) +#define ABS(j) (j < 0 ? -j : j) +#define INAREA(i, j, a) ((i) >= (a).x && (i) <= (a).x + (a).w && (j) >= (a).y && (j) <= (a).y + (a).h) + +/* + * "#RRGGBB" -> 0xRRGGBB + */ +static inline Color +color_atoh(const char *col) +{ + int shift = (col[0] == '#'); + + return (Color)strtol(col + shift, NULL, 16); +} + +static inline void +swap_ptr(void **x, void **y) +{ + void *t = *x; + + *x = *y; + *y = t; +} + +void *xmalloc(size_t nmemb, size_t size); +void *xcalloc(size_t nmemb, size_t size); +int xasprintf(char **strp, const char *fmt, ...); +char *xstrdup(const char *str); +pid_t spawn(const char *format, ...); +void uicb_spawn(Uicb cmd); + +#endif /* UTIL_H */ diff --git a/src/util.o b/src/util.o new file mode 100644 index 0000000000000000000000000000000000000000..377fbc61800656dccf7b750a2b38d233394b8d0e GIT binary patch literal 4712 zcmbVPUuaup6#ueT7j^Sy^WXdfyNnf+v1RFSQqhf0jNZ*us#4rbY18y}ElZP-n{-*B ztaXie4Oa>?IF#Xr6!tRgOJT^e1vY2}AIkQ!$2~ZV*oQd81)0`!&OKjqcbin$2RG-Q z-}gIzzWbf;+?!M3$h#Xno)*EwBMu6WR27v4kw{*3XyJy%HZ<0bWzbx-w5?yAEFUdwojwI%vY^v3E391)irVc zFGF;%_Fkv;Pc1LO8m7w;)7l10RRWg;2+b_j{zWZ+heT-!gk}~ONh{8Qh?x0{q|MHd zn4bipne#c)tbWq0qokedL@^zOMJRrDneWDMej+LXtDiLTtOSZF(rmLp@1)}AAo`5r zrv#P2=y}CYF_l1ePVrNQN?-z>V;&cxnRyLG!NM#x)9NIxhzY?$I!K$x^+3)c(n^?m zq>!(nu)3BaChVm-QH4j-fjq0E0`IVS(x+JyK9Jr+3x`@mu&6@|9v=v;U~h<4D53%F ziy2;q98fKUmk)qF<`M!`$v;OXJ!^{ynA+40+!w3?_x}56|1s3m3V0C8w7uUGHCYH% zS#TVoRpl^Da_u_{Cwyist1e%;W|kpGpL7d(kQADIM+{_?IbXp zB8_r@>YXHwa)%|%kVdH}fzo-NnxS_O{WrWFPr&RKttspF#_bc$~l1XmHad0 zVIDm=^P^%UUj;j!&BlzJG<(tvKPZGBz-hp$v^!#x^N_8D%4A=84Fa0sKkfR)+<4?h zqw#n$=5HI%?pEO^uv;}o#`V68njF*9L-FJZbx_afv5Y=Q7O_>jx{LVI5zAO|bTpb6 zR5Qt>8c!yUss4lN{`P(9z{!km1VlYrKtzpHdL)q<^0&c+kx36`Q*`^W7LG*3-hq+C zUSnA7i3vTY$KrltSlts-kvo)39~W%~MSPaydd5Ezo}ACqvT37t2Aaf9z+d6H3n*Iw z6i@p;+t7;d#?6l-1_OET(47$7DA(b|nKz5B*5Q4{ydIS6@UAfLdps!X@P1()6=oBj z2xjzLMg#|=nWzX37={R@lc)sY$+idEUw7#us1NrIrK6*|2quylJ^0=SJ$sByG@-fFL@O(7F$D82!CU~L=eyjvJCYIEk=Q_=Z4HY%4Ef@C z5-w*IcM~e;glvI(dac3B0@sHF%i?*sBtDHG4D{oy`ZI*C_4AVh$9E~z0 z<+|kaz2)HJdqnboW&A0?@Okcp3&*m9k6)OQ-^!aO`MZb>FCfM-A3URy-|oO)aNvg- zm(Oh<`{{A;w>tQ77k@wV4F~^a2YBU~6l89=>VN z-u*`NyB)FvE-&d0{;Lc4xBlq%SSuJ`))Cy&2yy(R1X%i5q79B6a5b|3j_tdMRsWH8 zQns_bvlftDWqYqAvJ3g%WmUWhH!ccI)z-ek33qef2h{!Sktpp?kyZW2ZIAC=ckal} zaiW)b66@{>xMcp=!*KM%)hK=l8wHH7ZL#|6j<3UvyC#sl$o5MdA7i7fJ3gMTM)qf) Qh6xxmwxHZ`OWgMV0Tg{n_y7O^ literal 0 HcmV?d00001 diff --git a/src/wmfs.c b/src/wmfs.c new file mode 100644 index 0000000..bba470e --- /dev/null +++ b/src/wmfs.c @@ -0,0 +1,344 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#include +#include +#include + +#include "wmfs.h" +#include "event.h" +#include "ewmh.h" +#include "screen.h" +#include "infobar.h" +#include "util.h" +#include "config.h" +#include "client.h" + +int +wmfs_error_handler(Display *d, XErrorEvent *event) +{ + char mess[256]; + + /* Check if there is another WM running */ + if(event->error_code == BadAccess + && W->root == event->resourceid) + errx(EXIT_FAILURE, "Another Window Manager is already running."); + + /* Ignore focus change error for unmapped client + * 42 = X_SetInputFocus + * 28 = X_GrabButton + */ + if(client_gb_win(event->resourceid)) + if(event->error_code == BadWindow + || event->request_code == 42 + || event->request_code == 28) + return 0; + + + if(XGetErrorText(d, event->error_code, mess, 128)) + warnx("%s(%d) opcodes %d/%d\n resource #%lx\n", + mess, + event->error_code, + event->request_code, + event->minor_code, + event->resourceid); + + return 1; +} + +int +wmfs_error_handler_dummy(Display *d, XErrorEvent *event) +{ + (void)d; + (void)event; + + return 0; +} + +void +wmfs_numlockmask(void) +{ + int i, j; + XModifierKeymap *mm = XGetModifierMapping(W->dpy); + + for(i = 0; i < 8; i++) + for(j = 0; j < mm->max_keypermod; ++j) + if(mm->modifiermap[i * mm->max_keypermod + j] + == XKeysymToKeycode(W->dpy, XK_Num_Lock)) + W->numlockmask = (1 << i); + + XFreeModifiermap(mm); +} + +void +wmfs_init_font(char *font, struct theme *t) +{ + XFontStruct **xfs = NULL; + char **misschar, **names, *defstring; + int d; + + if(!(t->font.fontset = XCreateFontSet(W->dpy, font, &misschar, &d, &defstring))) + { + warnx("Can't load font '%s'", font); + t->font.fontset = XCreateFontSet(W->dpy, "fixed", &misschar, &d, &defstring); + } + + XExtentsOfFontSet(t->font.fontset); + XFontsOfFontSet(t->font.fontset, &xfs, &names); + + t->font.as = xfs[0]->max_bounds.ascent; + t->font.de = xfs[0]->max_bounds.descent; + t->font.width = xfs[0]->max_bounds.width; + + t->font.height = t->font.as + t->font.de; + + if(misschar) + XFreeStringList(misschar); +} + +static void +wmfs_xinit(void) +{ + XSetWindowAttributes at = + { + .event_mask = (KeyMask | ButtonMask | MouseMask + | PropertyChangeMask | SubstructureRedirectMask + | SubstructureNotifyMask | StructureNotifyMask), + .cursor = XCreateFontCursor(W->dpy, XC_left_ptr) + }; + + /* + * X Error handler + */ + XSetErrorHandler(wmfs_error_handler); + + /* + * X var + */ + W->xscreen = DefaultScreen(W->dpy); + W->xdepth = DefaultDepth(W->dpy, W->xscreen); + W->gc = DefaultGC(W->dpy, W->xscreen); + + /* + * Keys + */ + wmfs_numlockmask(); + + /* + * Root window/cursor + */ + W->root = RootWindow(W->dpy, W->xscreen); + XChangeWindowAttributes(W->dpy, W->root, CWEventMask | CWCursor, &at); + + /* + * Locale (font encode) + */ + setlocale(LC_CTYPE, ""); + + /* + * Barwin linked list + */ + SLIST_INIT(&W->h.barwin); + + W->running = true; +} + +void +wmfs_grab_keys(void) +{ + KeyCode c; + struct keybind *k; + + wmfs_numlockmask(); + + XUngrabKey(W->dpy, AnyKey, AnyModifier, W->root); + + SLIST_FOREACH(k, &W->h.keybind, next) + if((c = XKeysymToKeycode(W->dpy, k->keysym))) + { + XGrabKey(W->dpy, c, k->mod, W->root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(W->dpy, c, k->mod | LockMask, W->root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(W->dpy, c, k->mod | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync); + XGrabKey(W->dpy, c, k->mod | LockMask | W->numlockmask, W->root, True, GrabModeAsync, GrabModeAsync); + } +} + +/** Scan if there are windows on X + * for manage it +*/ +static void +wmfs_scan(void) +{ + int i, n; + XWindowAttributes wa; + Window usl, usl2, *w = NULL; + + SLIST_INIT(&W->h.client); + + /* + Atom rt; + int s, rf, tag = -1, screen = -1, flags = -1, i; + ulong ir, il; + uchar *ret; + */ + + if(XQueryTree(W->dpy, W->root, &usl, &usl2, &w, (unsigned int*)&n)) + for(i = n - 1; i != -1; --i) + { + XGetWindowAttributes(W->dpy, w[i], &wa); + + if(!wa.override_redirect && wa.map_state == IsViewable) + {/* + if(XGetWindowProperty(dpy, w[i], ATOM("_WMFS_TAG"), 0, 32, + False, XA_CARDINAL, &rt, &rf, &ir, &il, &ret) == Success && ret) + { + tag = *ret; + XFree(ret); + } + + if(XGetWindowProperty(dpy, w[i], ATOM("_WMFS_SCREEN"), 0, 32, + False, XA_CARDINAL, &rt, &rf, &ir, &il, &ret) == Success && ret) + { + screen = *ret; + XFree(ret); + } + + if(XGetWindowProperty(dpy, w[i], ATOM("_WMFS_FLAGS"), 0, 32, + False, XA_CARDINAL, &rt, &rf, &ir, &il, &ret) == Success && ret) + { + flags = *ret; + XFree(ret); + } + */ + /*c = */ client_new(w[i], &wa); + + /* + if(tag != -1) + c->tag = tag; + if(screen != -1) + c->screen = screen; + if(flags != -1) + c->flags = flags; + */ + } + } + + XFree(w); +} + +static void +wmfs_loop(void) +{ + XEvent ev; + + while(XPending(W->dpy)) + while(W->running && !XNextEvent(W->dpy, &ev)) + EVENT_HANDLE(&ev); +} + +static inline void +wmfs_init(void) +{ + wmfs_xinit(); + ewmh_init(); + screen_init(); + event_init(); + config_init(); +} + +void +wmfs_quit(void) +{ + struct keybind *k; + struct theme *t; + + /* Will free: + * + * Screens -> tags + * -> Infobars -> Elements + */ + screen_free(); + + XCloseDisplay(W->dpy); + + /* Conf stuffs */ + while(!SLIST_EMPTY(&W->h.keybind)) + { + k = SLIST_FIRST(&W->h.keybind); + SLIST_REMOVE_HEAD(&W->h.keybind, next); + free((void*)k->cmd); + free(k); + } + + while(!SLIST_EMPTY(&W->h.theme)) + { + t = SLIST_FIRST(&W->h.theme); + SLIST_REMOVE_HEAD(&W->h.theme, next); + XFreeFontSet(W->dpy, t->font.fontset); + free(t); + } + + free(W->net_atom); + free(W); + + W->running = false; +} + +/** Reload WMFS binary +*/ +void +uicb_reload(Uicb cmd) +{ + (void)cmd; + /* TODO */ +} + +void +uicb_quit(Uicb cmd) +{ + (void)cmd; + W->running = false; +} + +int +main(int argc, char **argv) +{ + W = (struct wmfs*)xcalloc(1, sizeof(struct wmfs)); + + + /* Get X display */ + if(!(W->dpy = XOpenDisplay(NULL))) + { + fprintf(stderr, "%s: Can't open X server\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Opt */ + /* + int i; + while((i = getopt(argc, argv, "hviC:")) != -1) + { + switch(i) + { + case 'h': + break; + case 'v': + break; + case 'C': + break; + } + } + */ + + /* Core */ + wmfs_init(); + wmfs_scan(); + + wmfs_loop(); + + wmfs_quit(); + + return 1; +} diff --git a/src/wmfs.h b/src/wmfs.h new file mode 100644 index 0000000..be82658 --- /dev/null +++ b/src/wmfs.h @@ -0,0 +1,210 @@ +/* + * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } + * For license, see COPYING. + */ + +#ifndef WMFS_H +#define WMFS_H + +/* Standard */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Xlib */ +#include +#include + +/* Local */ + +#define ButtonMask (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask) +#define MouseMask (ButtonMask | PointerMotionMask) +#define KeyMask (KeyPressMask | KeyReleaseMask) + +typedef unsigned int Flags; +typedef unsigned int Color; +typedef const char* Uicb; +typedef enum { BarTop = 0, BarBottom, BarHide, BarLast } Barpos; +typedef enum { Right = 0, Left, Top, Bottom, Center, PositionLast } Position; + +/* + * Structures + */ + +struct geo +{ + int x, y, w, h; +}; + +struct barwin +{ + struct geo geo; + Window win; + Drawable dr; + Color fg, bg; + void *ptr; /* Special cases */ + SLIST_HEAD(, mousebind) mousebinds; + SLIST_ENTRY(barwin) next; /* global barwin */ + SLIST_ENTRY(barwin) enext; /* element barwin */ +}; + +struct element +{ + struct geo geo; + struct infobar *infobar; + int type; + void (*func_init)(struct element *e); + void (*func_update)(struct element *e); + SLIST_HEAD(, barwin) bars; + TAILQ_ENTRY(element) next; +}; + +struct infobar +{ + struct barwin *bar; + struct geo geo; + struct screen *screen; + struct theme *theme; + char *elemorder; + Barpos pos; + TAILQ_HEAD(esub, element) elements; + SLIST_ENTRY(infobar) next; +}; + +struct screen +{ + struct geo geo, ugeo; + struct tag *seltag; + int id; + Flags elemupdate; + TAILQ_HEAD(tsub, tag) tags; + SLIST_HEAD(, infobar) infobars; + SLIST_ENTRY(screen) next; +}; + +struct tag +{ + struct screen *screen; + struct client *sel; + char *name; + Flags flags; + Window frame; + SLIST_HEAD(, client) clients; + TAILQ_ENTRY(tag) next; +}; + +struct client +{ + struct tag *tag; + struct screen *screen; + struct barwin *titlebar; + struct geo geo, tgeo, wgeo; + char *title; + Flags flags; + Window win; + SLIST_ENTRY(client) next; /* Global list */ + SLIST_ENTRY(client) tnext; /* struct tag list */ +}; + +struct keybind +{ + unsigned int mod; + void (*func)(Uicb); + Uicb cmd; + KeySym keysym; + SLIST_ENTRY(keybind) next; +}; + +struct mousebind +{ + struct geo area; + unsigned int button; + bool use_area; + void (*func)(Uicb); + Uicb cmd; + SLIST_ENTRY(mousebind) next; +}; + +struct colpair +{ + Color fg, bg; +}; + +struct theme +{ + char *name; + + /* Font */ + struct + { + int as, de, width, height; + XFontSet fontset; + } font; + + /* Bars */ + struct colpair bars; + int bars_width; + + /* struct elements */ + struct colpair tags_n, tags_s; /* normal / selected */ + int tags_border_width; + Color tags_border_col; + + /* client / frame */ + struct colpair client_n, client_s; + Color frame_bg; + int client_titlebar_width; + int client_border_width; + + SLIST_ENTRY(theme) next; +}; + +struct wmfs +{ + /* X11 stuffs */ + Display *dpy; + Window root; + int xscreen, xdepth; + Flags numlockmask; + GC gc; + Atom *net_atom; + bool running; + + /* Lists heads */ + struct + { + SLIST_HEAD(, screen) screen; + SLIST_HEAD(, client) client; + SLIST_HEAD(, keybind) keybind; + SLIST_HEAD(, barwin) barwin; + SLIST_HEAD(, theme) theme; + } h; + + /* + * Selected screen, client + */ + struct screen *screen; + struct client *client; + +}; + +int wmfs_error_handler(Display *d, XErrorEvent *event); +int wmfs_error_handler_dummy(Display *d, XErrorEvent *event); +void wmfs_grab_keys(void); +void wmfs_numlockmask(void); +void wmfs_init_font(char *font, struct theme *t); +void wmfs_quit(void); +void uicb_reload(Uicb cmd); +void uicb_quit(Uicb cmd); + + +/* Single global variable */ +struct wmfs *W; + +#endif /* WMFS_H */ diff --git a/src/wmfs.o b/src/wmfs.o new file mode 100644 index 0000000000000000000000000000000000000000..4fdeb6b568d60988eac7484f39025285487f1f5c GIT binary patch literal 9784 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 a9c7*N@)}D3 literal 0 HcmV?d00001 diff --git a/wmfsrc2 b/wmfsrc2 new file mode 100644 index 0000000..7ee90d2 --- /dev/null +++ b/wmfsrc2 @@ -0,0 +1,149 @@ +# +# WMFS2 configuration file +# + +[themes] + + + [theme] + # name = "default" + + font = "fixed" + + # Bars + bars_width = 14 + bars_fg = "#CCCCCC" + bars_bg = "#222222" + + # Element tags + tags_normal_fg = "#CCCCCC" + tags_normal_bg = "#222222" + tags_sel_fg = "#222222" + tags_sel_bg = "#CCCCCC" + tags_border_color = "#888888" + tags_border_width = 1 + + # Frame / Client + client_normal_fg = "#CCCCCC" + client_normal_bg = "#222222" + client_sel_fg = "#222222" + client_sel_bg = "#CCCCCC" + frame_bg = "#555555" + client_titlebar_width = 12 #useless for now + client_border_width = 1 + + [/theme] + + [theme] + name = "perso" + + font = "-*-fixed-bold" + + bars_width = 20 + bars_fg = "#222222" + bars_bg = "#CCCCCC" + + tags_sel_bg = "#33AA33" + tags_normal_fg = "#AA3333" + + [/theme] + +[/themes] + +[bars] + + # Position: + # 0 Top + # 1 Bottom + # 2 Hide + + # Element type: + # t Tags + # S Statustext + + [bar] + position = 1 + screen = 0 + elements = "t" + theme = "perso" + [/bar] + + [bar] + position = 1 + screen = 0 + elements = "t" + theme = "default" + [/bar] + + [bar] + screen = 1 + elements = "t" + theme = "perso" + [/bar] + [bar] + screen = 1 + elements = "t" + theme = "default" + [/bar] + + +[/bars] + +[tags] + + [tag] screen = 0 name = "one" [/tag] + [tag] screen = 0 name = "two" [/tag] + [tag] screen = 0 name = "three" [/tag] + + [tag] screen = 1 name = "four" [/tag] + [tag] screen = 1 name = "five" [/tag] + + [tag] name = "universal tag" [/tag] + +[/tags] + +[keys] + + [key] mod = {"Super"} key = "Return" func = "spawn" cmd = "xterm" [/key] + [key] mod = {"Control","Alt"} key = "q" func = "quit" [/key] + [key] mod = {"Super"} key = "1" func = "tag_set" cmd = "1" [/key] + [key] mod = {"Super"} key = "2" func = "tag_set" cmd = "2" [/key] + [key] mod = {"Super"} key = "3" func = "tag_set" cmd = "3" [/key] + [key] mod = {"Super"} key = "s" func = "tag_next" [/key] + [key] mod = {"Super"} key = "a" func = "tag_prev" [/key] + [key] mod = {"Super"} key = "z" func = "tag" cmd = "tag2" [/key] + + [key] mod = {"Super"} key = "q" func = "client_close" [/key] + + # Focus next / prev client + [key] mod = { "Alt" } key = "Tab" func = "client_focus_next" [/key][key] mod = { "Alt", "Shift" } key = "Tab" func = "client_focus_prev" [/key] + + # Focus next client with direction + [key] mod = {"Alt"} key = "h" func = "client_focus_left" [/key] + [key] mod = {"Alt"} key = "l" func = "client_focus_right" [/key] + [key] mod = {"Alt"} key = "k" func = "client_focus_top" [/key] + [key] mod = {"Alt"} key = "j" func = "client_focus_bottom" [/key] + + # swap next client with direction: + [key] mod = {"Control", "Shift"} key = "h" func = "client_swap_left" [/key] + [key] mod = {"Control", "Shift"} key = "l" func = "client_swap_right" [/key] + [key] mod = {"Control", "Shift"} key = "k" func = "client_swap_top" [/key] + [key] mod = {"Control", "Shift"} key = "j" func = "client_swap_bottom" [/key] + + # Resize selected tiled client with direction + [key] mod = {"Super"} key = "h" func = "client_resize_left" cmd = "20" [/key] + [key] mod = {"Super"} key = "l" func = "client_resize_left" cmd = "-20" [/key] + [key] mod = {"Super"} key = "k" func = "client_resize_top" cmd = "20" [/key] + [key] mod = {"Super"} key = "j" func = "client_resize_top" cmd = "-20" [/key] + [key] mod = {"Super", "Control"} key = "h" func = "client_resize_right" cmd = "-20" [/key] + [key] mod = {"Super", "Control"} key = "l" func = "client_resize_right" cmd = "20" [/key] + [key] mod = {"Super", "Control"} key = "k" func = "client_resize_bottom" cmd = "-20" [/key] + [key] mod = {"Super", "Control"} key = "j" func = "client_resize_bottom" cmd = "20" [/key] + + # Layout manipulation + [key] mod = {"Super"} key = "m" func = "layout_vmirror" [/key] + [key] mod = {"Super", "Shift"} key = "m" func = "layout_hmirror" [/key] + [key] mod = {"Super"} key = "r" func = "layout_rotate_right" [/key] + [key] mod = {"Super", "Shift"} key = "r" func = "layout_rotate_left" [/key] + +[/keys]