From d7c08095ed378b72710dfb61808fd78ca9384bfb Mon Sep 17 00:00:00 2001 From: kanishk Date: Tue, 12 May 2026 18:16:49 +0530 Subject: [PATCH] tab with pertag --- config.def.h | 7 ++ dwm.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 226 insertions(+), 22 deletions(-) diff --git a/config.def.h b/config.def.h index 81c3fc0..925979b 100644 --- a/config.def.h +++ b/config.def.h @@ -5,8 +5,13 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int snap = 32; /* snap pixel */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ +static const int showtab = 1; /* 0 means no tabbar */ +static const int alltab = 0; /* 0 means no tabbar for all layouts */ +static const int toptab = 0; /* 1 means top tab bar */ static const char *fonts[] = { "monospace:size=10" }; static const char dmenufont[] = "monospace:size=10"; + + static const char col_gray1[] = "#222222"; static const char col_gray2[] = "#444444"; static const char col_gray3[] = "#bbbbbb"; @@ -66,6 +71,7 @@ static const Key keys[] = { { MODKEY, XK_p, spawn, {.v = dmenucmd } }, { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_w, toggletab, {0} }, // tabmodes { MODKEY, XK_j, focusstack, {.i = +1 } }, { MODKEY, XK_k, focusstack, {.i = -1 } }, { MODKEY, XK_i, incnmaster, {.i = +1 } }, @@ -113,5 +119,6 @@ static const Button buttons[] = { { ClkTagBar, 0, Button3, toggleview, {0} }, { ClkTagBar, MODKEY, Button1, tag, {0} }, { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + { ClkTabBar, 0, Button1, focuswin, {0} }, // tab mode }; diff --git a/dwm.c b/dwm.c index fc3365c..cd9c6a6 100644 --- a/dwm.c +++ b/dwm.c @@ -56,6 +56,8 @@ #define TAGMASK ((1 << LENGTH(tags)) - 1) #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ @@ -63,7 +65,7 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, +enum { ClkTagBar,ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ typedef union { @@ -110,6 +112,8 @@ typedef struct { void (*arrange)(Monitor *); } Layout; +#define MAXTABS 50 // tab + typedef struct Pertag Pertag; struct Monitor { char ltsymbol[16]; @@ -117,18 +121,24 @@ struct Monitor { int nmaster; int num; int by; /* bar geometry */ + int ty; /* tab bar geometry */ int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ unsigned int seltags; unsigned int sellt; unsigned int tagset[2]; int showbar; + int showtab;// tab int topbar; + int toptab;// tab Client *clients; Client *sel; Client *stack; Monitor *next; Window barwin; + Window tabwin;// tab + int ntabs;// tab + int tab_widths[MAXTABS];// tab const Layout *lt[2]; Pertag *pertag; }; @@ -164,12 +174,15 @@ static void detachstack(Client *c); static Monitor *dirtomon(int dir); static void drawbar(Monitor *m); static void drawbars(void); +static void drawtab(Monitor *m);// tab +static void drawtabs(void);// tab static void enternotify(XEvent *e); static void expose(XEvent *e); static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void focuswin(const Arg* arg);// tab static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); @@ -207,6 +220,7 @@ static void setup(void); static void seturgent(Client *c, int urg); static void showhide(Client *c); static void spawn(const Arg *arg); +static void toggletab(const Arg *arg);// tab static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); @@ -241,6 +255,7 @@ static char stext[256]; static int screen; static int sw, sh; /* X display screen geometry width, height */ static int bh; /* bar height */ +static int th = 0; /* tab bar geometry */// tab static int lrpad; /* sum of left and right padding for text */ static int (*xerrorxlib)(Display *, XErrorEvent *); static unsigned int numlockmask = 0; @@ -279,6 +294,7 @@ struct Pertag { unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ + int showtabs[LENGTH(tags) + 1]; /* display tab for the current tag */ }; /* compile-time check if all tags fit into an unsigned int bit array. */ @@ -406,6 +422,8 @@ arrange(Monitor *m) void arrangemon(Monitor *m) { + updatebarpos(selmon);//tab + XMoveResizeWindow(dpy, selmon->tabwin, selmon->wx, selmon->ty, selmon->ww, th);//tab strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); @@ -451,11 +469,28 @@ buttonpress(XEvent *e) arg.ui = 1 << i; } else if (ev->x < x + TEXTW(selmon->ltsymbol)) click = ClkLtSymbol; - else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2) + else if (ev->x > selmon->ww - (int)TEXTW(stext)) click = ClkStatusText; else click = ClkWinTitle; - } else if ((c = wintoclient(ev->window))) { + } + if(ev->window == selmon->tabwin) {//tab + i = 0; x = 0;//tab + for(c = selmon->clients; c; c = c->next){//tab + if(!ISVISIBLE(c)) continue;//tab + x += selmon->tab_widths[i];//tab + if (ev->x > x)//tab + ++i;//tab + else//tab + break;//tab + if(i >= m->ntabs) break;//tab + }//tab + if(c) {//tab + click = ClkTabBar;//tab + arg.ui = i;//tab + } + } + else if((c = wintoclient(ev->window))) { focus(c); restack(selmon); XAllowEvents(dpy, ReplayPointer, CurrentTime); @@ -463,8 +498,9 @@ buttonpress(XEvent *e) } for (i = 0; i < LENGTH(buttons); i++) if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button - && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) - buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){//tab + buttons[i].func(((click == ClkTagBar || click == ClkTabBar) && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);//tab + } } void @@ -519,6 +555,8 @@ cleanupmon(Monitor *mon) } XUnmapWindow(dpy, mon->barwin); XDestroyWindow(dpy, mon->barwin); + XUnmapWindow(dpy, mon->tabwin);//tab + XDestroyWindow(dpy, mon->tabwin);//tab free(mon); } @@ -651,7 +689,10 @@ createmon(void) m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; + m->showtab = showtab;//tab m->topbar = topbar; + m->toptab = toptab;//tab + m->ntabs = 0;//tab m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); @@ -667,6 +708,7 @@ createmon(void) m->pertag->sellts[i] = m->sellt; m->pertag->showbars[i] = m->showbar; + m->pertag->showtabs[i] = m->showtab; } return m; @@ -782,6 +824,106 @@ drawbars(void) drawbar(m); } +void +drawtabs(void) { + + Monitor *m; + + for(m = mons; m; m = m->next) + drawtab(m); +} + +static int +cmpint(const void *p1, const void *p2) { + /* The actual arguments to this function are "pointers to + pointers to char", but strcmp(3) arguments are "pointers + to char", hence the following cast plus dereference */ + return *((int*) p1) > * (int*) p2; +} + + +void +drawtab(Monitor *m) { + Client *c; + int i; + int itag = -1; + char view_info[50]; + int view_info_w = 0; + int sorted_label_widths[MAXTABS]; + int tot_width; + int maxsize = bh; + int x = 0; + int w = 0; + + //view_info: indicate the tag which is displayed in the view + for(i = 0; i < LENGTH(tags); ++i){ + if((selmon->tagset[selmon->seltags] >> i) & 1) { + if(itag >=0){ //more than one tag selected + itag = -1; + break; + } + itag = i; + } + } + + if(0 <= itag && itag < LENGTH(tags)){ + snprintf(view_info, sizeof view_info, "[%s]", tags[itag]); + } else { + strncpy(view_info, "[...]", sizeof view_info); + } + view_info[sizeof(view_info) - 1 ] = 0; + view_info_w = TEXTW(view_info); + tot_width = view_info_w; + + /* Calculates number of labels and their width */ + m->ntabs = 0; + for(c = m->clients; c; c = c->next){ + if(!ISVISIBLE(c)) continue; + m->tab_widths[m->ntabs] = TEXTW(c->name); + tot_width += m->tab_widths[m->ntabs]; + ++m->ntabs; + if(m->ntabs >= MAXTABS) break; + } + + if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated + memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs); + qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint); + tot_width = view_info_w; + for(i = 0; i < m->ntabs; ++i){ + if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww) + break; + tot_width += sorted_label_widths[i]; + } + maxsize = (m->ww - tot_width) / (m->ntabs - i); + } else{ + maxsize = m->ww; + } + i = 0; + for(c = m->clients; c; c = c->next){ + if(!ISVISIBLE(c)) continue; + if(i >= m->ntabs) break; + if(m->tab_widths[i] > maxsize) m->tab_widths[i] = maxsize; + w = m->tab_widths[i]; + drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]); + drw_text(drw, x, 0, w, th, 0, c->name, 0); + x += w; + ++i; + } + + drw_setscheme(drw, scheme[SchemeNorm]); + + /* cleans interspace between window names and current viewed tag label */ + w = m->ww - view_info_w - x; + drw_text(drw, x, 0, w, th, 0, "", 0); + + /* view info */ + x += w; + w = view_info_w; + drw_text(drw, x, 0, w, th, 0, view_info, 0); + + drw_map(drw, m->tabwin, 0, 0, m->ww, th); +} + void enternotify(XEvent *e) { @@ -807,8 +949,10 @@ expose(XEvent *e) Monitor *m; XExposeEvent *ev = &e->xexpose; - if (ev->count == 0 && (m = wintomon(ev->window))) + if (ev->count == 0 && (m = wintomon(ev->window))){ drawbar(m); + drawtab(m);//tab + } } void @@ -834,6 +978,7 @@ focus(Client *c) } selmon->sel = c; drawbars(); + drawtabs();//tab } /* there are some broken focus acquiring clients needing extra handling */ @@ -886,18 +1031,31 @@ focusstack(const Arg *arg) } } +void +focuswin(const Arg* arg){ + int iwin = arg->i; + Client* c = NULL; + for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){ + if(ISVISIBLE(c)) --iwin; + }; + if(c) { + focus(c); + restack(selmon); + } +} + Atom getatomprop(Client *c, Atom prop) { - int format; + int di; unsigned long nitems, dl; unsigned char *p = NULL; Atom da, atom = None; if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, - &da, &format, &nitems, &dl, &p) == Success && p) { - if (nitems > 0 && format == 32) - atom = *(long *)p; + &da, &di, &nitems, &dl, &p) == Success && p) { + if (nitems > 0) + atom = *(Atom *)p; XFree(p); } return atom; @@ -923,10 +1081,10 @@ getstate(Window w) Atom real; if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], - &real, &format, &n, &extra, &p) != Success) + &real, &format, &n, &extra, (unsigned char **)&p) != Success) return -1; - if (n != 0 && format == 32) - result = *(long *)p; + if (n != 0) + result = *p; XFree(p); return result; } @@ -1269,12 +1427,14 @@ propertynotify(XEvent *e) case XA_WM_HINTS: updatewmhints(c); drawbars(); + drawtabs();//tab break; } if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { updatetitle(c); if (c == c->mon->sel) drawbar(c->mon); + drawtab(c->mon);//tab } if (ev->atom == netatom[NetWMWindowType]) updatewindowtype(c); @@ -1388,6 +1548,7 @@ restack(Monitor *m) XWindowChanges wc; drawbar(m); + drawtab(m); if (!m->sel) return; if (m->sel->isfloating || !m->lt[m->sellt]->arrange) @@ -1402,7 +1563,8 @@ restack(Monitor *m) } } XSync(dpy, False); - while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)) + ; } void @@ -1455,8 +1617,6 @@ sendmon(Client *c, Monitor *m) c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ attach(c); attachstack(c); - if (c->isfullscreen) - resizeclient(c, m->mx, m->my, m->mw, m->mh); focus(NULL); arrange(NULL); } @@ -1498,10 +1658,12 @@ sendevent(Client *c, Atom proto) void setfocus(Client *c) { - if (!c->neverfocus) + if (!c->neverfocus) { XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, - PropModeReplace, (unsigned char *)&c->win, 1); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } sendevent(c, wmatom[WMTakeFocus]); } @@ -1589,6 +1751,7 @@ setup(void) die("no fonts could be loaded."); lrpad = drw->fonts->h; bh = drw->fonts->h + 2; + th = bh;//tab updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); @@ -1747,6 +1910,13 @@ togglebar(const Arg *arg) arrange(selmon); } + void +toggletab(const Arg *arg) +{ + selmon->showtab= selmon->pertag->showtabs[selmon->pertag->curtag]=!selmon->showtab; + arrange(selmon); +} + void togglefloating(const Arg *arg) { @@ -1807,6 +1977,9 @@ toggleview(const Arg *arg) if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) togglebar(NULL); + if (selmon->showtab != selmon->pertag->showtabs[selmon->pertag->curtag]) + toggletab(NULL); + focus(NULL); arrange(selmon); } @@ -1883,6 +2056,11 @@ updatebars(void) CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); XMapRaised(dpy, m->barwin); + m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->tabwin); XSetClassHint(dpy, m->barwin, &ch); } } @@ -1890,14 +2068,31 @@ updatebars(void) void updatebarpos(Monitor *m) { + Client *c;//tab + int nvis = 0;//tab m->wy = m->my; m->wh = m->mh; if (m->showbar) { m->wh -= bh; m->by = m->topbar ? m->wy : m->wy + m->wh; - m->wy = m->topbar ? m->wy + bh : m->wy; - } else + if ( m->topbar ) m->wy += bh;//tab + } else {//tab m->by = -bh; + } + + for(c = m->clients; c; c = c->next) {//tab + if(ISVISIBLE(c)) ++nvis;//tab + }//tab +//this check condition to show the tabbar coditions showtab_always and others. +// if(m->showtab == showtab_always || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))) + if((m->showtab==1 && alltab==1) || (m->showtab== 1 && ( nvis >1) && (m->lt[m->sellt]->arrange==monocle )) ){//tab + m->wh -= th;//tab + m->ty = m->toptab ? m->wy : m->wy + m->wh;//tab + if ( m->toptab )//tab + m->wy += th;//tab + } else {//tab + m->ty = -th;//tab + }//tab } void @@ -2134,6 +2329,8 @@ view(const Arg *arg) if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) togglebar(NULL); + if (selmon->showtab != selmon->pertag->showbars[selmon->pertag->curtag]) + toggletab(NULL); focus(NULL); arrange(selmon); } @@ -2161,7 +2358,7 @@ wintomon(Window w) if (w == root && getrootptr(&x, &y)) return recttomon(x, y, 1, 1); for (m = mons; m; m = m->next) - if (w == m->barwin) + if (w == m->barwin || w == m->tabwin)// condition for tab return m; if ((c = wintoclient(w))) return c->mon; -- 2.54.0