diff --git a/Makefile b/Makefile index 7350d9ef..8fd08c92 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC = client.c draw.c event.c layout.c awesome.c tag.c util.c config.c screen.c statusbar.c uicb.c +SRC = client.c draw.c event.c layout.c awesome.c tag.c util.c config.c screen.c statusbar.c uicb.c tab.c OBJ = ${SRC:.c=.o} ${LAYOUTS:.c=.o} all: options awesome diff --git a/awesomerc b/awesomerc index ad5eb3c5..db7ea2a7 100644 --- a/awesomerc +++ b/awesomerc @@ -18,6 +18,7 @@ colors focus_border = "#6666ff" focus_bg = "#6666ff" focus_fg = "#ffffff" + tab_border = "#ff0000" } statusbar @@ -559,4 +560,28 @@ keys command = "toggletag" arg = "9" } + key + { + modkey = {"Mod4"} + key = "u" + command = "viewprevtab" + } + key + { + modkey = {"Mod4"} + key = "i" + command = "viewnexttab" + } + key + { + modkey = {"Mod4", "Control"} + key = "u" + command = "untab" + } + key + { + modkey = {"Mod4", "Control"} + key = "i" + command = "tab" + } } diff --git a/client.c b/client.c index 3c2bd323..3a681e60 100644 --- a/client.c +++ b/client.c @@ -320,7 +320,10 @@ focus(Display *disp, Client * c, Bool selscreen, awesome_config *awesomeconf) drawstatusbar(disp, awesomeconf); if(*awesomeconf->client_sel) { - XSetWindowBorder(awesomeconf->display, (*awesomeconf->client_sel)->win, awesomeconf->colors_selected[ColBorder].pixel); + if((*awesomeconf->client_sel)->tab.next || (*awesomeconf->client_sel)->tab.prev) + XSetWindowBorder(awesomeconf->display, (*awesomeconf->client_sel)->win, awesomeconf->colors_tab[ColBorder].pixel); + else + XSetWindowBorder(awesomeconf->display, (*awesomeconf->client_sel)->win, awesomeconf->colors_selected[ColBorder].pixel); XSetInputFocus(awesomeconf->display, (*awesomeconf->client_sel)->win, RevertToPointerRoot, CurrentTime); for(c = *awesomeconf->clients; c; c = c->next) if(c != *awesomeconf->client_sel) @@ -388,6 +391,7 @@ manage(Display *disp, Window w, XWindowAttributes *wa, awesome_config *awesomeco c->oldborder = wa->border_width; c->display = disp; c->phys_screen = get_phys_screen(c->display, c->screen); + c->tab.isvisible = True; screen_info = get_screen_info(c->display, c->screen, NULL); if(c->w == screen_info[c->screen].width && c->h == screen_info[c->screen].height) { diff --git a/config.c b/config.c index b9851b05..0ffc9661 100644 --- a/config.c +++ b/config.c @@ -33,6 +33,7 @@ #include "tag.h" #include "statusbar.h" #include "layout.h" +#include "tab.h" #include "layouts/tile.h" #include "layouts/floating.h" #include "layouts/max.h" @@ -91,6 +92,11 @@ const NameFuncLink UicbList[] = { /* statusbar.c */ {"togglebar", uicb_togglebar}, {"setstatustext", uicb_setstatustext}, + /* tab.c */ + {"tab", uicb_tab}, + {"untab", uicb_untab}, + {"viewnexttab", uicb_viewnexttab}, + {"viewprevtab", uicb_viewprevtab}, {NULL, NULL} }; @@ -161,6 +167,7 @@ parse_config(Display * disp, int scr,const char *confpatharg, awesome_config *aw CFG_STR((char *) "focus_border", (char *) "#6666ff", CFGF_NONE), CFG_STR((char *) "focus_bg", (char *) "#6666ff", CFGF_NONE), CFG_STR((char *) "focus_fg", (char *) "#ffffff", CFGF_NONE), + CFG_STR((char *) "tab_border", (char *) "#ff0000", CFGF_NONE), CFG_END() }; static cfg_opt_t statusbar_opts[] = @@ -291,6 +298,7 @@ parse_config(Display * disp, int scr,const char *confpatharg, awesome_config *aw awesomeconf->colors_selected[ColBorder] = initxcolor(disp, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_border")); awesomeconf->colors_selected[ColBG] = initxcolor(disp, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_bg")); awesomeconf->colors_selected[ColFG] = initxcolor(disp, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "focus_fg")); + awesomeconf->colors_tab[ColBorder] = initxcolor(disp, awesomeconf->phys_screen, cfg_getstr(cfg_colors, "tab_border")); /* Statusbar */ tmp = cfg_getstr(cfg_statusbar, "position"); diff --git a/config.h b/config.h index eabbcbf4..4e688dda 100644 --- a/config.h +++ b/config.h @@ -115,6 +115,17 @@ struct Client Client *next; /** Previous client */ Client *prev; + /** Tabs support */ + struct + { + /** Next client in tab */ + Client *next; + /** Previous client in tab */ + Client *prev; + /** True if client is the visible one */ + Bool isvisible; + /** True if client is tabbed */ + } tab; /** Window of the client */ Window win; /** Client display */ @@ -190,6 +201,8 @@ struct awesome_config XColor colors_normal[ColLast]; /** Selected colors */ XColor colors_selected[ColLast]; + /** Tabbed colors */ + XColor colors_tab[ColLast]; /** Cursors */ Cursor cursor[CurLast]; /** Font */ diff --git a/event.c b/event.c index d26c07b6..8f3fdf8b 100644 --- a/event.c +++ b/event.c @@ -38,8 +38,8 @@ #define CLEANMASK(mask, screen) (mask & ~(awesomeconf[screen].numlockmask | LockMask)) #define MOUSEMASK (BUTTONMASK | PointerMotionMask) -static Client * -getclient(Client **list, Window w) +Client * +get_client_bywin(Client **list, Window w) { Client *c; @@ -195,7 +195,7 @@ handle_event_buttonpress(XEvent * e, awesome_config *awesomeconf) return; } - if((c = getclient(awesomeconf->clients, ev->window))) + if((c = get_client_bywin(awesomeconf->clients, ev->window))) { focus(c->display, c, ev->same_screen, &awesomeconf[c->screen]); if(CLEANMASK(ev->state, c->screen) != awesomeconf[c->screen].modkey) @@ -255,7 +255,7 @@ handle_event_configurerequest(XEvent * e, awesome_config *awesomeconf) XConfigureRequestEvent *ev = &e->xconfigurerequest; XWindowChanges wc; - if((c = getclient(awesomeconf->clients, ev->window))) + if((c = get_client_bywin(awesomeconf->clients, ev->window))) { c->ismax = False; if(ev->value_mask & CWBorderWidth) @@ -338,7 +338,7 @@ handle_event_destroynotify(XEvent * e, awesome_config *awesomeconf) Client *c; XDestroyWindowEvent *ev = &e->xdestroywindow; - if((c = getclient(awesomeconf->clients, ev->window))) + if((c = get_client_bywin(awesomeconf->clients, ev->window))) unmanage(c, WithdrawnState, &awesomeconf[c->screen]); } @@ -351,7 +351,7 @@ handle_event_enternotify(XEvent * e, awesome_config *awesomeconf) if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) return; - if((c = getclient(awesomeconf->clients, ev->window))) + if((c = get_client_bywin(awesomeconf->clients, ev->window))) { if(!*awesomeconf->client_sel || *awesomeconf->client_sel != c) { @@ -444,7 +444,7 @@ handle_event_maprequest(XEvent * e, awesome_config *awesomeconf) return; if(wa.override_redirect) return; - if(!getclient(awesomeconf->clients, ev->window)) + if(!get_client_bywin(awesomeconf->clients, ev->window)) { for(screen = 0; wa.screen != ScreenOfDisplay(e->xany.display, screen); screen++); if(screen == 0) @@ -469,13 +469,13 @@ handle_event_propertynotify(XEvent * e, awesome_config *awesomeconf) if(ev->state == PropertyDelete) return; /* ignore */ - if((c = getclient(awesomeconf->clients, ev->window))) + if((c = get_client_bywin(awesomeconf->clients, ev->window))) { switch (ev->atom) { case XA_WM_TRANSIENT_FOR: XGetTransientForHint(e->xany.display, c->win, &trans); - if(!c->isfloating && (c->isfloating = (getclient(awesomeconf->clients, trans) != NULL))) + if(!c->isfloating && (c->isfloating = (get_client_bywin(awesomeconf->clients, trans) != NULL))) arrange(e->xany.display, &awesomeconf[c->screen]); break; case XA_WM_NORMAL_HINTS: @@ -497,7 +497,7 @@ handle_event_unmapnotify(XEvent * e, awesome_config *awesomeconf) Client *c; XUnmapEvent *ev = &e->xunmap; - if((c = getclient(awesomeconf->clients, ev->window)) + if((c = get_client_bywin(awesomeconf->clients, ev->window)) && ev->event == RootWindow(e->xany.display, c->phys_screen) && (ev->send_event || !c->unmapped)) unmanage(c, WithdrawnState, &awesomeconf[c->screen]); } @@ -507,7 +507,7 @@ handle_event_shape(XEvent * e, awesome_config *awesomeconf __attribute__ ((unused))) { XShapeEvent *ev = (XShapeEvent *) e; - Client *c = getclient(awesomeconf->clients, ev->window); + Client *c = get_client_bywin(awesomeconf->clients, ev->window); if(c) set_shape(c); diff --git a/event.h b/event.h index b60de013..49101d59 100644 --- a/event.h +++ b/event.h @@ -24,6 +24,7 @@ #include "config.h" +Client * get_client_bywin(Client **list, Window w); void grabkeys(Display *, int, awesome_config *); /* grab all keys defined in config */ void handle_event_buttonpress(XEvent *, awesome_config *); diff --git a/tab.c b/tab.c new file mode 100644 index 00000000..737a3c04 --- /dev/null +++ b/tab.c @@ -0,0 +1,127 @@ +/* + * tab.c - tab management + * + * Copyright © 2007 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "tab.h" +#include "event.h" +#include "layout.h" + +void +uicb_tab(awesome_config *awesomeconf, + const char *arg __attribute__ ((unused))) +{ + Window dummy, child; + int x1, y1, di; + unsigned int dui; + XEvent ev; + Client *sel = *awesomeconf->client_sel, *c = NULL, *tmp; + + if(XGrabPointer(awesomeconf->display, RootWindow(awesomeconf->display, awesomeconf->phys_screen), + False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, + awesomeconf[awesomeconf->screen].cursor[CurMove], CurrentTime) != GrabSuccess) + return; + + + for(;;) + { + XMaskEvent(awesomeconf->display, ButtonPressMask, &ev); + if(ev.type == ButtonPress) + { + XUngrabPointer(awesomeconf->display, CurrentTime); + break; + } + } + XQueryPointer(awesomeconf->display, + RootWindow(awesomeconf->display, awesomeconf->phys_screen), + &dummy, &child, &x1, &y1, &di, &di, &dui); + if((c = get_client_bywin(awesomeconf->clients, child)) + && c != sel) + { + /* take the last tabbed window */ + for(tmp = sel; tmp->tab.next; tmp = tmp->tab.next); + tmp->tab.next = c; + c->tab.prev = tmp; + + c->tab.isvisible = False; + arrange(awesomeconf->display, awesomeconf); + } +} + +void +uicb_untab(awesome_config *awesomeconf, + const char *arg __attribute__ ((unused))) +{ + Client *tmp, *sel = *awesomeconf->client_sel; + + if(!sel) + return; + + if(sel->tab.next) + sel->tab.next->tab.isvisible = True; + else if(sel->tab.prev) + sel->tab.prev->tab.isvisible = True; + + sel->tab.isvisible = True; + + if(sel->tab.next) + sel->tab.next->tab.prev = sel->tab.prev; + + tmp = sel->tab.next; + sel->tab.next = NULL; + + if(sel->tab.prev) + sel->tab.prev->tab.next = tmp; + + sel->tab.prev = NULL; + + arrange(awesomeconf->display, awesomeconf); +} + +void +uicb_viewnexttab(awesome_config *awesomeconf, + const char *arg __attribute__ ((unused))) +{ + Client *sel = *awesomeconf->client_sel; + + if(!sel || !sel->tab.next) + return; + + sel->tab.isvisible = False; + sel->tab.next->tab.isvisible = True; + arrange(awesomeconf->display, awesomeconf); + focus(awesomeconf->display, sel->tab.next, True, awesomeconf); +} + +void +uicb_viewprevtab(awesome_config *awesomeconf, + const char *arg __attribute__ ((unused))) +{ + Client *sel = *awesomeconf->client_sel; + + if(!sel || !sel->tab.prev) + return; + + sel->tab.isvisible = False; + sel->tab.prev->tab.isvisible = True; + arrange(awesomeconf->display, awesomeconf); + focus(awesomeconf->display, sel->tab.prev, True, awesomeconf); +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 diff --git a/tab.h b/tab.h new file mode 100644 index 00000000..dc0af900 --- /dev/null +++ b/tab.h @@ -0,0 +1,33 @@ +/* + * tab.h - tab management header + * + * Copyright © 2007 Julien Danjou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_TAB_H +#define AWESOME_TAB_H + +#include "common.h" + +UICB_PROTO(uicb_tab); +UICB_PROTO(uicb_untab); +UICB_PROTO(uicb_viewnexttab); +UICB_PROTO(uicb_viewprevtab); + +#endif +// vim: filetype=c:expandtab:shiftwidth=6:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 diff --git a/tag.c b/tag.c index b9952a8d..a08b5d7e 100644 --- a/tag.c +++ b/tag.c @@ -129,7 +129,7 @@ isvisible(Client * c, int screen, Tag * tags, int ntags) { int i; - if(c->screen != screen) + if(c->screen != screen || !c->tab.isvisible) return False; for(i = 0; i < ntags; i++)