From 078d224b46770e5ea03142b5058088a8903289a3 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Fri, 14 Dec 2007 14:29:32 +0100 Subject: [PATCH] use a linked list for handling tag <-> client relationship --- client.c | 114 +++++++++++++++++++++++---------------------- client.h | 3 +- config.h | 12 ++++- layout.c | 14 +++--- layouts/floating.c | 2 +- layouts/max.c | 2 +- layouts/tile.c | 4 +- statusbar.c | 17 ++++--- tag.c | 113 ++++++++++++++++++++++++++++++++++++-------- tag.h | 7 ++- 10 files changed, 190 insertions(+), 98 deletions(-) diff --git a/client.c b/client.c index c8326ada..b99e4c7d 100644 --- a/client.c +++ b/client.c @@ -33,18 +33,39 @@ #include "window.h" #include "layouts/floating.h" -/** Get a Client by its window - * \param list Client list to look info - * \param w Client window to find - * \return client +/** Load windows properties, restoring client's tag + * and floating state before awesome was restarted if any + * \todo this may bug if number of tags is != than before + * \param c Client ref + * \param ntags tags number */ -Client * -get_client_bywin(Client *list, Window w) +static Bool +loadprops(Client * c, VirtScreen *scr) { - Client *c; + int i; + char *prop; + Bool result = False; - for(c = list; c && c->win != w; c = c->next); - return c; + prop = p_new(char, scr->ntags + 2); + + if(xgettextprop(c->display, c->win, AWESOMEPROPS_ATOM(c->display), prop, scr->ntags + 2)) + { + for(i = 0; i < scr->ntags && prop[i]; i++) + if(prop[i] == '1') + { + tag_client(&scr->tclink, c, &scr->tags[i]); + result = True; + } + else + untag_client(&scr->tclink, c, &scr->tags[i]); + + if(i <= scr->ntags && prop[i]) + c->isfloating = prop[i] == '1'; + } + + p_delete(&prop); + + return result; } /** Check if client supports protocol WM_DELETE_WINDOW @@ -101,6 +122,20 @@ client_swap(Client **head, Client *c1, Client *c2) *head = c2; } +/** Get a Client by its window + * \param list Client list to look info + * \param w Client window to find + * \return client + */ +Client * +get_client_bywin(Client *list, Window w) +{ + Client *c; + + for(c = list; c && c->win != w; c = c->next); + return c; +} + void updatetitle(Client *c) { @@ -180,8 +215,8 @@ focus(Client *c, Bool selscreen, awesome_config *awesomeconf, int screen) Tag *tag = get_current_tag(awesomeconf->screens[screen]); /* if c is NULL or invisible, take next client in the stack */ - if((!c && selscreen) || (c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags))) - for(c = awesomeconf->clients; c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); c = c->next); + if((!c && selscreen) || (c && !isvisible(c, &awesomeconf->screens[screen], screen))) + for(c = awesomeconf->clients; c && !isvisible(c, &awesomeconf->screens[screen], screen); c = c->next); /* XXX unfocus other tags clients, this is a bit too much */ for(i = 0; i < awesomeconf->screens[screen].ntags; i++) @@ -221,36 +256,6 @@ focus(Client *c, Bool selscreen, awesome_config *awesomeconf, int screen) XSetInputFocus(awesomeconf->display, RootWindow(awesomeconf->display, get_phys_screen(awesomeconf->display, screen)), RevertToPointerRoot, CurrentTime); } - -/** Load windows properties, restoring client's tag - * and floating state before awesome was restarted if any - * \todo this may bug if number of tags is != than before - * \param c Client ref - * \param ntags tags number - */ -Bool -loadprops(Client * c, int ntags) -{ - int i; - char *prop; - Bool result = False; - - prop = p_new(char, ntags + 2); - - if(xgettextprop(c->display, c->win, AWESOMEPROPS_ATOM(c->display), prop, ntags + 2)) - { - for(i = 0; i < ntags && prop[i]; i++) - if((c->tags[i] = prop[i] == '1')) - result = True; - if(i <= ntags && prop[i]) - c->isfloating = prop[i] == '1'; - } - - p_delete(&prop); - - return result; -} - /** Manage a new client * \param w The window * \param wa Window attributes @@ -285,7 +290,7 @@ client_manage(Window w, XWindowAttributes *wa, awesome_config *awesomeconf, int updatetitle(c); /* loadprops or apply rules if no props */ - if(!loadprops(c, awesomeconf->screens[screen].ntags)) + if(!loadprops(c, &awesomeconf->screens[screen])) tag_client_with_rules(c, awesomeconf); screen_info = get_screen_info(awesomeconf->display, screen, NULL, NULL); @@ -346,14 +351,15 @@ client_manage(Window w, XWindowAttributes *wa, awesome_config *awesomeconf, int if((rettrans = XGetTransientForHint(c->display, w, &trans) == Success) && (t = get_client_bywin(awesomeconf->clients, trans))) for(i = 0; i < awesomeconf->screens[c->screen].ntags; i++) - c->tags[i] = t->tags[i]; + if(is_client_tagged(awesomeconf->screens[c->screen].tclink, t, &awesomeconf->screens[c->screen].tags[i])) + tag_client(&awesomeconf->screens[c->screen].tclink, c, &awesomeconf->screens[c->screen].tags[i]); /* should be floating if transsient or fixed) */ if(!c->isfloating) c->isfloating = (rettrans == Success) || c->isfixed; /* save new props */ - saveprops(c, awesomeconf->screens[c->screen].ntags); + saveprops(c, &awesomeconf->screens[c->screen]); /* attach to the stack */ client_attach(&awesomeconf->clients, c); @@ -457,17 +463,17 @@ client_resize(Client *c, int x, int y, int w, int h, awesome_config *awesomeconf } void -saveprops(Client * c, int ntags) +saveprops(Client * c, VirtScreen *scr) { int i; char *prop; - prop = p_new(char, ntags + 2); + prop = p_new(char, scr->ntags + 2); - for(i = 0; i < ntags; i++) - prop[i] = c->tags[i] ? '1' : '0'; + for(i = 0; i < scr->ntags; i++) + prop[i] = is_client_tagged(scr->tclink, c, &scr->tags[i]) ? '1' : '0'; - if(i <= ntags) + if(i <= scr->ntags) prop[i] = c->isfloating ? '1' : '0'; prop[++i] = '\0'; @@ -508,7 +514,6 @@ client_unmanage(Client *c, long state, awesome_config *awesomeconf) XUngrabServer(c->display); if(state != NormalState) arrange(awesomeconf, c->screen); - p_delete(&c->tags); p_delete(&c); } @@ -595,10 +600,9 @@ tag_client_with_rules(Client *c, awesome_config *awesomeconf) if(is_tag_match_rules(&awesomeconf->screens[c->screen].tags[i], r)) { matched = True; - c->tags[i] = True; + tag_client(&awesomeconf->screens[c->screen].tclink, c, + &awesomeconf->screens[c->screen].tags[i]); } - else - c->tags[i] = False; if(!matched) tag_client_with_current_selected(c, &awesomeconf->screens[c->screen]); @@ -685,7 +689,7 @@ uicb_client_swapnext(awesome_config *awesomeconf, if(!sel) return; - for(next = sel->next; next && !isvisible(next, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); next = next->next); + for(next = sel->next; next && !isvisible(next, &awesomeconf->screens[screen], screen); next = next->next); if(next) { client_swap(&awesomeconf->clients, sel, next); @@ -705,7 +709,7 @@ uicb_client_swapprev(awesome_config *awesomeconf, if(!sel) return; - for(prev = sel->prev; prev && !isvisible(prev, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); prev = prev->prev); + for(prev = sel->prev; prev && !isvisible(prev, &awesomeconf->screens[screen], screen); prev = prev->prev); if(prev) { client_swap(&awesomeconf->clients, prev, sel); diff --git a/client.h b/client.h index e5db05e8..e4957e29 100644 --- a/client.h +++ b/client.h @@ -28,7 +28,6 @@ Client * get_client_bywin(Client *, Window); void client_attach(Client **, Client *); void client_detach(Client **, Client *); void client_reattach_after(Client *, Client *); -Bool loadprops(Client *, int ); void client_ban(Client *); void focus(Client *, Bool, awesome_config *, int); void client_manage(Window, XWindowAttributes *, awesome_config *, int); @@ -37,7 +36,7 @@ void client_unban(Client *); void client_unmanage(Client *, long, awesome_config *); void updatesizehints(Client *); void updatetitle(Client *); -void saveprops(Client *, int); +void saveprops(Client *, VirtScreen *); void tag_client_with_rules(Client *, awesome_config *); UICB_PROTO(uicb_client_kill); diff --git a/config.h b/config.h index fd73aa6b..57a1247f 100644 --- a/config.h +++ b/config.h @@ -116,8 +116,6 @@ struct Client Bool isfixed; /** True if the window is maximized */ Bool ismax; - /** Tags for the client */ - Bool *tags; /** Next client */ Client *next; /** Previous client */ @@ -153,6 +151,15 @@ typedef struct int ncol; } Tag; +/** TagClientLink type */ +typedef struct TagClientLink TagClientLink; +struct TagClientLink +{ + Tag *tag; + Client *client; + TagClientLink *next; +}; + /** Padding type */ typedef struct { @@ -190,6 +197,7 @@ typedef struct Tag *tags; /** Number of tags in **tags */ int ntags; + TagClientLink *tclink; /** Layout list */ Layout *layouts; int nlayouts; diff --git a/layout.c b/layout.c index 9879c7a1..23fbcfe6 100644 --- a/layout.c +++ b/layout.c @@ -57,7 +57,7 @@ arrange(awesome_config *awesomeconf, int screen) for(c = awesomeconf->clients; c; c = c->next) { - if(isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags)) + if(isvisible(c, &awesomeconf->screens[screen], screen)) client_unban(c); /* we don't touch other screens windows */ else if(c->screen == screen) @@ -89,9 +89,9 @@ uicb_client_focusnext(awesome_config * awesomeconf, if(!sel) return; - for(c = sel->next; c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); c = c->next); + for(c = sel->next; c && !isvisible(c, &awesomeconf->screens[screen], screen); c = c->next); if(!c) - for(c = awesomeconf->clients; c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); c = c->next); + for(c = awesomeconf->clients; c && !isvisible(c, &awesomeconf->screens[screen], screen); c = c->next); if(c) { focus(c, True, awesomeconf, screen); @@ -108,11 +108,11 @@ uicb_client_focusprev(awesome_config *awesomeconf, if(!sel) return; - for(c = sel->prev; c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); c = c->prev); + for(c = sel->prev; c && !isvisible(c, &awesomeconf->screens[screen], screen); c = c->prev); if(!c) { for(c = awesomeconf->clients; c && c->next; c = c->next); - for(; c && !isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); c = c->prev); + for(; c && !isvisible(c, &awesomeconf->screens[screen], screen); c = c->prev); } if(c) { @@ -168,7 +168,7 @@ restack(awesome_config *awesomeconf, int screen) } for(c = awesomeconf->clients; c; c = c->next) { - if(!IS_TILED(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags) || c == sel) + if(!IS_TILED(c, &awesomeconf->screens[screen], screen) || c == sel) continue; XConfigureWindow(awesomeconf->display, c->win, CWSibling | CWStackMode, &wc); wc.sibling = c->win; @@ -309,7 +309,7 @@ uicb_client_zoom(awesome_config *awesomeconf, Client *sel = get_current_tag(awesomeconf->screens[screen])->client_sel; if(awesomeconf->clients == sel) - for(sel = sel->next; sel && !isvisible(sel, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags); sel = sel->next); + for(sel = sel->next; sel && !isvisible(sel, &awesomeconf->screens[screen], screen); sel = sel->next); if(!sel) return; diff --git a/layouts/floating.c b/layouts/floating.c index 165640c2..cf869329 100644 --- a/layouts/floating.c +++ b/layouts/floating.c @@ -28,7 +28,7 @@ layout_floating(awesome_config *awesomeconf, int screen) Client *c; for(c = awesomeconf->clients; c; c = c->next) - if(isvisible(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags)) + if(isvisible(c, &awesomeconf->screens[screen], screen)) client_resize(c, c->rx, c->ry, c->rw, c->rh, awesomeconf, True, False); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 diff --git a/layouts/max.c b/layouts/max.c index cdc6cb52..eec7f09e 100644 --- a/layouts/max.c +++ b/layouts/max.c @@ -31,7 +31,7 @@ layout_max(awesome_config *awesomeconf, int screen) ScreenInfo *si = get_screen_info(awesomeconf->display, screen, &awesomeconf->screens[screen].statusbar, &awesomeconf->screens[screen].padding); for(c = awesomeconf->clients; c; c = c->next) - if(IS_TILED(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags)) + if(IS_TILED(c, &awesomeconf->screens[screen], screen)) client_resize(c, si[screen].x_org, si[screen].y_org, si[screen].width - 2 * c->border, si[screen].height - 2 * c->border, awesomeconf, awesomeconf->screens[screen].resize_hints, False); diff --git a/layouts/tile.c b/layouts/tile.c index 7fbcd415..0cf6d4dd 100644 --- a/layouts/tile.c +++ b/layouts/tile.c @@ -110,7 +110,7 @@ _tile(awesome_config *awesomeconf, int screen, const Bool right) screens_info = get_screen_info(awesomeconf->display, screen, &awesomeconf->screens[screen].statusbar, &awesomeconf->screens[screen].padding); for(n = 0, c = awesomeconf->clients; c; c = c->next) - if(IS_TILED(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags)) + if(IS_TILED(c, &awesomeconf->screens[screen], screen)) n++; wah = screens_info[screen].height; @@ -137,7 +137,7 @@ _tile(awesome_config *awesomeconf, int screen, const Bool right) for(i = 0, c = awesomeconf->clients; c; c = c->next) { - if(!IS_TILED(c, screen, awesomeconf->screens[screen].tags, awesomeconf->screens[screen].ntags)) + if(!IS_TILED(c, &awesomeconf->screens[screen], screen)) continue; c->ismax = False; diff --git a/statusbar.c b/statusbar.c index edb0bb4b..02a6d025 100644 --- a/statusbar.c +++ b/statusbar.c @@ -27,6 +27,7 @@ #include "draw.h" #include "screen.h" #include "util.h" +#include "tag.h" #include "layouts/tile.h" /** Check if at least a client is tagged with tag number t and is on screen @@ -36,12 +37,12 @@ * \return True or False */ static Bool -isoccupied(Client *head, unsigned int t, int screen) +isoccupied(TagClientLink *tc, int screen, Client *head, Tag *t) { Client *c; for(c = head; c; c = c->next) - if(c->tags[t] && c->screen == screen) + if(c->screen == screen && is_client_tagged(tc, c, t)) return True; return False; } @@ -78,7 +79,8 @@ drawstatusbar(awesome_config *awesomeconf, int screen) awesomeconf->screens[screen].statusbar.height, awesomeconf->screens[screen].font, awesomeconf->screens[screen].tags[i].name, awesomeconf->screens[screen].colors_selected); - if(isoccupied(awesomeconf->clients, i, screen)) + if(isoccupied(awesomeconf->screens[screen].tclink, screen, awesomeconf->clients, + &awesomeconf->screens[screen].tags[i])) drawrectangle(awesomeconf->display, phys_screen, x, y, (awesomeconf->screens[screen].font->height + 2) / 4, @@ -86,7 +88,8 @@ drawstatusbar(awesome_config *awesomeconf, int screen) drawable, awesomeconf->screens[screen].statusbar.width, awesomeconf->screens[screen].statusbar.height, - sel && sel->tags[i], + sel && is_client_tagged(awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[i]), awesomeconf->screens[screen].colors_selected[ColFG]); } else @@ -99,7 +102,8 @@ drawstatusbar(awesome_config *awesomeconf, int screen) awesomeconf->screens[screen].statusbar.height, awesomeconf->screens[screen].font, awesomeconf->screens[screen].tags[i].name, awesomeconf->screens[screen].colors_normal); - if(isoccupied(awesomeconf->clients, i, screen)) + if(isoccupied(awesomeconf->screens[screen].tclink, screen, + awesomeconf->clients, &awesomeconf->screens[screen].tags[i])) drawrectangle(awesomeconf->display, phys_screen, x, y, (awesomeconf->screens[screen].font->height + 2) / 4, @@ -107,7 +111,8 @@ drawstatusbar(awesome_config *awesomeconf, int screen) drawable, awesomeconf->screens[screen].statusbar.width, awesomeconf->screens[screen].statusbar.height, - sel && sel->tags[i], + sel && is_client_tagged(awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[i]), awesomeconf->screens[screen].colors_normal[ColFG]); } x += w; diff --git a/tag.c b/tag.c index 3e1c5e12..481266ae 100644 --- a/tag.c +++ b/tag.c @@ -28,23 +28,81 @@ #include "util.h" #include "rules.h" + +static void +detach_tagclientlink(TagClientLink **head, TagClientLink *tc) +{ + TagClientLink *tmp; + + if(*head == tc) + *head = tc->next; + else + { + for(tmp = *head; tmp && tmp->next != tc; tmp = tmp->next); + tmp->next = tc->next; + } + + p_delete(&tc); +} + +void +tag_client(TagClientLink **head, Client *c, Tag *t) +{ + TagClientLink *tc, *new_tc; + + /* don't tag twice */ + if(is_client_tagged(*head, c, t)) + return; + + new_tc = p_new(TagClientLink, 1); + + if(!*head) + *head = new_tc; + else + { + for(tc = *head; tc->next; tc = tc->next); + tc->next = new_tc; + } + + new_tc->client = c; + new_tc->tag = t; +} + +void +untag_client(TagClientLink **head, Client *c, Tag *t) +{ + TagClientLink *tc; + + for(tc = *head; tc; tc = tc->next) + if(tc->client == c && tc->tag == t) + detach_tagclientlink(head, tc); +} + +Bool +is_client_tagged(TagClientLink *head, Client *c, Tag *t) +{ + TagClientLink *tc; + + for(tc = head; tc; tc = tc->next) + if(tc->client == c && tc->tag == t) + return True; + return False; +} + /** Returns True if a client is tagged * with one of the tags - * \param c Client - * \param tags tag to check - * \param ntags number of tags in *tags * \return True or False */ Bool -isvisible(Client * c, int screen, Tag * tags, int ntags) +isvisible(Client *c, VirtScreen *scr, int screen) { int i; if(c->screen != screen) return False; - for(i = 0; i < ntags; i++) - if(c->tags[i] && tags[i].selected) + for(i = 0; i < scr->ntags; i++) + if(is_client_tagged(scr->tclink, c, &scr->tags[i]) && scr->tags[i].selected) return True; return False; } @@ -54,9 +112,11 @@ tag_client_with_current_selected(Client *c, VirtScreen *screen) { int i; - p_realloc(&c->tags, screen->ntags); for(i = 0; i < screen->ntags; i++) - c->tags[i] = screen->tags[i].selected; + if(screen->tags[i].selected) + tag_client(&screen->tclink, c, &screen->tags[i]); + else + untag_client(&screen->tclink, c, &screen->tags[i]); } /** Tag selected window with tag @@ -81,13 +141,19 @@ uicb_client_tag(awesome_config *awesomeconf, return; } - for(i = 0; i < awesomeconf->screens[screen].ntags; i++) - sel->tags[i] = arg == NULL; - + if(arg) + for(i = 0; i < awesomeconf->screens[screen].ntags; i++) + untag_client(&awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[i]); + else + for(i = 0; i < awesomeconf->screens[screen].ntags; i++) + tag_client(&awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[i]); if(tag_id != -1) - sel->tags[tag_id] = True; + tag_client(&awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[tag_id]); - saveprops(sel, awesomeconf->screens[screen].ntags); + saveprops(sel, &awesomeconf->screens[screen]); arrange(awesomeconf, screen); } @@ -112,7 +178,7 @@ uicb_client_togglefloating(awesome_config * awesomeconf, else client_resize(sel, sel->x, sel->y, sel->w, sel->h, awesomeconf, True, True); - saveprops(sel, awesomeconf->screens[screen].ntags); + saveprops(sel, &awesomeconf->screens[screen]); arrange(awesomeconf, screen); } @@ -138,18 +204,25 @@ uicb_client_toggletag(awesome_config *awesomeconf, if(i < 0 || i >= awesomeconf->screens[screen].ntags) return; - sel->tags[i] = !sel->tags[i]; + if(is_client_tagged(awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[i])) + untag_client(&awesomeconf->screens[screen].tclink, sel,&awesomeconf->screens[screen].tags[i]); + else + tag_client(&awesomeconf->screens[screen].tclink, sel,&awesomeconf->screens[screen].tags[i]); /* check that there's at least one tag selected for this client*/ - for(j = 0; j < awesomeconf->screens[screen].ntags && !sel->tags[j]; j++); - if(j == awesomeconf->screens[screen].ntags) - sel->tags[i] = True; + for(j = 0; j < awesomeconf->screens[screen].ntags + && !is_client_tagged(awesomeconf->screens[screen].tclink, sel, + &awesomeconf->screens[screen].tags[j]); j++); + + if(j == awesomeconf->screens[screen].ntags) + tag_client(&awesomeconf->screens[screen].tclink, sel,&awesomeconf->screens[screen].tags[i]); } else for(i = 0; i < awesomeconf->screens[screen].ntags; i++) - sel->tags[i] = True; + tag_client(&awesomeconf->screens[screen].tclink, sel,&awesomeconf->screens[screen].tags[i]); - saveprops(sel, awesomeconf->screens[screen].ntags); + saveprops(sel, &awesomeconf->screens[screen]); arrange(awesomeconf, screen); } diff --git a/tag.h b/tag.h index 0818ab48..9275e3a2 100644 --- a/tag.h +++ b/tag.h @@ -25,9 +25,12 @@ #include "client.h" /** Check if a client is tiled */ -#define IS_TILED(client, screen, tags, ntags) (client && !client->isfloating && isvisible(client, screen, tags, ntags)) +#define IS_TILED(client, scr, screen) (client && !client->isfloating && isvisible(client, scr, screen)) -Bool isvisible(Client *, int, Tag *, int); +Bool isvisible(Client *, VirtScreen *, int); +void tag_client(TagClientLink **, Client *, Tag *); +void untag_client(TagClientLink **, Client *, Tag *); +Bool is_client_tagged(TagClientLink *, Client *, Tag *); void tag_client_with_current_selected(Client *, VirtScreen *); UICB_PROTO(uicb_client_tag);