dwm-6.1-tab-v2b.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       dwm-6.1-tab-v2b.diff (15707B)
       ---
            1 diff --git a/config.def.h b/config.def.h
            2 index 7054c06..e784231 100644
            3 --- a/config.def.h
            4 +++ b/config.def.h
            5 @@ -15,6 +15,12 @@ static const unsigned int borderpx  = 1;        /* border pixel of windows */
            6  static const unsigned int snap      = 32;       /* snap pixel */
            7  static const int showbar            = 1;        /* 0 means no bar */
            8  static const int topbar             = 1;        /* 0 means bottom bar */
            9 +/*   Display modes of the tab bar: never shown, always shown, shown only in */
           10 +/*   monocle mode in presence of several windows.                           */
           11 +/*   Modes after showtab_nmodes are disabled                                */
           12 +enum showtab_modes { showtab_never, showtab_auto, showtab_nmodes, showtab_always};
           13 +static const int showtab            = showtab_auto; /* Default tab bar show mode */
           14 +static const int toptab            = False;    /* False means bottom tab bar */
           15  
           16  /* tagging */
           17  static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
           18 @@ -62,6 +68,7 @@ static Key keys[] = {
           19          { MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
           20          { MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
           21          { MODKEY,                       XK_b,      togglebar,      {0} },
           22 +        { MODKEY,                       XK_w,      tabmode,        {-1} },
           23          { MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
           24          { MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
           25          { MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
           26 @@ -109,5 +116,6 @@ static Button buttons[] = {
           27          { ClkTagBar,            0,              Button3,        toggleview,     {0} },
           28          { ClkTagBar,            MODKEY,         Button1,        tag,            {0} },
           29          { ClkTagBar,            MODKEY,         Button3,        toggletag,      {0} },
           30 +        { ClkTabBar,            0,              Button1,        focuswin,       {0} },
           31  };
           32  
           33 diff --git a/dwm.1 b/dwm.1
           34 index 6687011..9ff827c 100644
           35 --- a/dwm.1
           36 +++ b/dwm.1
           37 @@ -19,14 +19,22 @@ layout applied.
           38  Windows are grouped by tags. Each window can be tagged with one or multiple
           39  tags. Selecting certain tags displays all windows with these tags.
           40  .P
           41 -Each screen contains a small status bar which displays all available tags, the
           42 -layout, the title of the focused window, and the text read from the root window
           43 -name property, if the screen is focused. A floating window is indicated with an
           44 -empty square and a maximised floating window is indicated with a filled square
           45 -before the windows title.  The selected tags are indicated with a different
           46 -color. The tags of the focused window are indicated with a filled square in the
           47 -top left corner.  The tags which are applied to one or more windows are
           48 -indicated with an empty square in the top left corner.
           49 +Each screen contains two small status bars. 
           50 +.P
           51 +One bar displays all available tags, the layout, the title of the focused
           52 +window, and the text read from the root window name property, if the screen is
           53 +focused. A floating window is indicated with an empty square and a maximised
           54 +floating window is indicated with a filled square before the windows title.  The
           55 +selected tags are indicated with a different color. The tags of the focused
           56 +window are indicated with a filled square in the top left corner.  The tags
           57 +which are applied to one or more windows are indicated with an empty square in
           58 +the top left corner. 
           59 +.P
           60 +Another bar contains a tab for each window of the current view and allows
           61 +navigation between windows, especially in the monocle mode. The different
           62 +display modes of this bar are described under the Mod1\-w Keybord command
           63 +section.  When a single tag is selected, this tag is indicated in the left corner
           64 +of the tab bar.
           65  .P
           66  dwm draws a small border around windows to indicate the focus state.
           67  .SH OPTIONS
           68 @@ -43,7 +51,8 @@ command.
           69  .TP
           70  .B Button1
           71  click on a tag label to display all windows with that tag, click on the layout
           72 -label toggles between tiled and floating layout.
           73 +label toggles between tiled and floating layout, click on a window name in the
           74 +tab bar brings focus to that window.
           75  .TP
           76  .B Button3
           77  click on a tag label adds/removes all windows with that tag to/from the view.
           78 @@ -104,6 +113,12 @@ Increase master area size.
           79  .B Mod1\-h
           80  Decrease master area size.
           81  .TP
           82 +.B Mod1\-w
           83 +Cycle over the tab bar display modes: never displayed, always displayed,
           84 +displayed only in monocle mode when the view contains more than one window (auto
           85 +mode). Some display modes can be disabled in the configuration, config.h. In
           86 +the default configuration only "never" and "auto" display modes are enabled.
           87 +.TP
           88  .B Mod1\-Return
           89  Zooms/cycles focused window to/from master area (tiled layouts only).
           90  .TP
           91 diff --git a/dwm.c b/dwm.c
           92 index 0362114..ff06772 100644
           93 --- a/dwm.c
           94 +++ b/dwm.c
           95 @@ -64,7 +64,7 @@ enum { NetSupported, NetWMName, NetWMState,
           96         NetWMFullscreen, NetActiveWindow, NetWMWindowType,
           97         NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
           98  enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
           99 -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
          100 +enum { ClkTagBar, ClkTabBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
          101         ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
          102  
          103  typedef union {
          104 @@ -111,24 +111,32 @@ typedef struct {
          105          void (*arrange)(Monitor *);
          106  } Layout;
          107  
          108 +#define MAXTABS 50
          109 +
          110  struct Monitor {
          111          char ltsymbol[16];
          112          float mfact;
          113          int nmaster;
          114          int num;
          115          int by;               /* bar geometry */
          116 +        int ty;               /* tab bar geometry */
          117          int mx, my, mw, mh;   /* screen size */
          118          int wx, wy, ww, wh;   /* window area  */
          119          unsigned int seltags;
          120          unsigned int sellt;
          121          unsigned int tagset[2];
          122          int showbar;
          123 +        int showtab;
          124          int topbar;
          125 +        int toptab;
          126          Client *clients;
          127          Client *sel;
          128          Client *stack;
          129          Monitor *next;
          130          Window barwin;
          131 +        Window tabwin;
          132 +        int ntabs;
          133 +        int tab_widths[MAXTABS];
          134          const Layout *lt[2];
          135  };
          136  
          137 @@ -164,12 +172,15 @@ static void detachstack(Client *c);
          138  static Monitor *dirtomon(int dir);
          139  static void drawbar(Monitor *m);
          140  static void drawbars(void);
          141 +static void drawtab(Monitor *m);
          142 +static void drawtabs(void);
          143  static void enternotify(XEvent *e);
          144  static void expose(XEvent *e);
          145  static void focus(Client *c);
          146  static void focusin(XEvent *e);
          147  static void focusmon(const Arg *arg);
          148  static void focusstack(const Arg *arg);
          149 +static void focuswin(const Arg* arg);
          150  static int getrootptr(int *x, int *y);
          151  static long getstate(Window w);
          152  static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
          153 @@ -206,6 +217,7 @@ static void setup(void);
          154  static void showhide(Client *c);
          155  static void sigchld(int unused);
          156  static void spawn(const Arg *arg);
          157 +static void tabmode(const Arg *arg);
          158  static void tag(const Arg *arg);
          159  static void tagmon(const Arg *arg);
          160  static void tile(Monitor *);
          161 @@ -240,6 +252,7 @@ static char stext[256];
          162  static int screen;
          163  static int sw, sh;           /* X display screen geometry width, height */
          164  static int bh, blw = 0;      /* bar geometry */
          165 +static int th = 0;           /* tab bar geometry */
          166  static int (*xerrorxlib)(Display *, XErrorEvent *);
          167  static unsigned int numlockmask = 0;
          168  static void (*handler[LASTEvent]) (XEvent *) = {
          169 @@ -391,8 +404,9 @@ arrange(Monitor *m)
          170  }
          171  
          172  void
          173 -arrangemon(Monitor *m)
          174 -{
          175 +arrangemon(Monitor *m) {
          176 +        updatebarpos(m);
          177 +        XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
          178          strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
          179          if (m->lt[m->sellt]->arrange)
          180                  m->lt[m->sellt]->arrange(m);
          181 @@ -442,14 +456,33 @@ buttonpress(XEvent *e)
          182                          click = ClkStatusText;
          183                  else
          184                          click = ClkWinTitle;
          185 -        } else if ((c = wintoclient(ev->window))) {
          186 +        }
          187 +        if(ev->window == selmon->tabwin) {
          188 +                i = 0; x = 0;
          189 +                for(c = selmon->clients; c; c = c->next){
          190 +                  if(!ISVISIBLE(c)) continue;
          191 +                  x += selmon->tab_widths[i];
          192 +                  if (ev->x > x)
          193 +                    ++i;
          194 +                  else
          195 +                    break;
          196 +                  if(i >= m->ntabs) break;
          197 +                }
          198 +                if(c) {
          199 +                  click = ClkTabBar;
          200 +                  arg.ui = i;
          201 +                }
          202 +        }
          203 +        else if((c = wintoclient(ev->window))) {
          204                  focus(c);
          205                  click = ClkClientWin;
          206          }
          207          for (i = 0; i < LENGTH(buttons); i++)
          208                  if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
          209 -                && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
          210 -                        buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
          211 +                && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)){
          212 +                        buttons[i].func(((click == ClkTagBar || click == ClkTabBar)
          213 +                                   && buttons[i].arg.i == 0) ? &arg : &buttons[i].arg);
          214 +                }
          215  }
          216  
          217  void
          218 @@ -505,6 +538,8 @@ cleanupmon(Monitor *mon)
          219          }
          220          XUnmapWindow(dpy, mon->barwin);
          221          XDestroyWindow(dpy, mon->barwin);
          222 +        XUnmapWindow(dpy, mon->tabwin);
          223 +        XDestroyWindow(dpy, mon->tabwin);
          224          free(mon);
          225  }
          226  
          227 @@ -565,6 +600,7 @@ void
          228  configurenotify(XEvent *e)
          229  {
          230          Monitor *m;
          231 +        Client *c;
          232          XConfigureEvent *ev = &e->xconfigure;
          233          int dirty;
          234  
          235 @@ -576,8 +612,12 @@ configurenotify(XEvent *e)
          236                  if (updategeom() || dirty) {
          237                          drw_resize(drw, sw, bh);
          238                          updatebars();
          239 -                        for (m = mons; m; m = m->next)
          240 +                        for(m = mons; m; m = m->next){
          241 +                                for (c = m->clients; c; c = c->next)
          242 +                                        if (c->isfullscreen)
          243 +                                                resizeclient(c, m->mx, m->my, m->mw, m->mh);
          244                                  XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
          245 +                        }
          246                          focus(NULL);
          247                          arrange(NULL);
          248                  }
          249 @@ -646,7 +686,10 @@ createmon(void)
          250          m->mfact = mfact;
          251          m->nmaster = nmaster;
          252          m->showbar = showbar;
          253 +        m->showtab = showtab;
          254          m->topbar = topbar;
          255 +        m->toptab = toptab;
          256 +        m->ntabs = 0;
          257          m->lt[0] = &layouts[0];
          258          m->lt[1] = &layouts[1 % LENGTH(layouts)];
          259          strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
          260 @@ -763,6 +806,105 @@ drawbars(void)
          261  }
          262  
          263  void
          264 +drawtabs(void) {
          265 +        Monitor *m;
          266 +
          267 +        for(m = mons; m; m = m->next)
          268 +                drawtab(m);
          269 +}
          270 +
          271 +static int
          272 +cmpint(const void *p1, const void *p2) {
          273 +  /* The actual arguments to this function are "pointers to
          274 +     pointers to char", but strcmp(3) arguments are "pointers
          275 +     to char", hence the following cast plus dereference */
          276 +  return *((int*) p1) > * (int*) p2;
          277 +}
          278 +
          279 +
          280 +void
          281 +drawtab(Monitor *m) {
          282 +        Client *c;
          283 +        int i;
          284 +        int itag = -1;
          285 +        char view_info[50];
          286 +        int view_info_w = 0;
          287 +        int sorted_label_widths[MAXTABS];
          288 +        int tot_width;
          289 +        int maxsize = bh;
          290 +        int x = 0;
          291 +        int w = 0;
          292 +
          293 +        //view_info: indicate the tag which is displayed in the view
          294 +        for(i = 0; i < LENGTH(tags); ++i){
          295 +          if((selmon->tagset[selmon->seltags] >> i) & 1) {
          296 +            if(itag >=0){ //more than one tag selected
          297 +              itag = -1;
          298 +              break;
          299 +            }
          300 +            itag = i;
          301 +          }
          302 +        }
          303 +
          304 +        if(0 <= itag  && itag < LENGTH(tags)){
          305 +          snprintf(view_info, sizeof view_info, "[%s]", tags[itag]);
          306 +        } else {
          307 +          strncpy(view_info, "[...]", sizeof view_info);
          308 +        }
          309 +        view_info[sizeof(view_info) - 1 ] = 0;
          310 +        view_info_w = TEXTW(view_info);
          311 +        tot_width = view_info_w;
          312 +
          313 +        /* Calculates number of labels and their width */
          314 +        m->ntabs = 0;
          315 +        for(c = m->clients; c; c = c->next){
          316 +          if(!ISVISIBLE(c)) continue;
          317 +          m->tab_widths[m->ntabs] = TEXTW(c->name);
          318 +          tot_width += m->tab_widths[m->ntabs];
          319 +          ++m->ntabs;
          320 +          if(m->ntabs >= MAXTABS) break;
          321 +        }
          322 +
          323 +        if(tot_width > m->ww){ //not enough space to display the labels, they need to be truncated
          324 +          memcpy(sorted_label_widths, m->tab_widths, sizeof(int) * m->ntabs);
          325 +          qsort(sorted_label_widths, m->ntabs, sizeof(int), cmpint);
          326 +          tot_width = view_info_w;
          327 +          for(i = 0; i < m->ntabs; ++i){
          328 +            if(tot_width + (m->ntabs - i) * sorted_label_widths[i] > m->ww)
          329 +              break;
          330 +            tot_width += sorted_label_widths[i];
          331 +          }
          332 +          maxsize = (m->ww - tot_width) / (m->ntabs - i);
          333 +        } else{
          334 +          maxsize = m->ww;
          335 +        }
          336 +        i = 0;
          337 +        for(c = m->clients; c; c = c->next){
          338 +          if(!ISVISIBLE(c)) continue;
          339 +          if(i >= m->ntabs) break;
          340 +          if(m->tab_widths[i] >  maxsize) m->tab_widths[i] = maxsize;
          341 +          w = m->tab_widths[i];
          342 +          drw_setscheme(drw, (c == m->sel) ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
          343 +          drw_text(drw, x, 0, w, th, c->name, 0);
          344 +          x += w;
          345 +          ++i;
          346 +        }
          347 +
          348 +        drw_setscheme(drw, &scheme[SchemeNorm]);
          349 +
          350 +        /* cleans interspace between window names and current viewed tag label */
          351 +        w = m->ww - view_info_w - x;
          352 +        drw_text(drw, x, 0, w, th, "", 0);
          353 +
          354 +        /* view info */
          355 +        x += w;
          356 +        w = view_info_w;
          357 +        drw_text(drw, x, 0, w, th, view_info, 0);
          358 +
          359 +        drw_map(drw, m->tabwin, 0, 0, m->ww, th);
          360 +}
          361 +
          362 +void
          363  enternotify(XEvent *e)
          364  {
          365          Client *c;
          366 @@ -787,8 +929,10 @@ expose(XEvent *e)
          367          Monitor *m;
          368          XExposeEvent *ev = &e->xexpose;
          369  
          370 -        if (ev->count == 0 && (m = wintomon(ev->window)))
          371 +        if(ev->count == 0 && (m = wintomon(ev->window))){
          372                  drawbar(m);
          373 +                drawtab(m);
          374 +        }
          375  }
          376  
          377  void
          378 @@ -815,6 +959,7 @@ focus(Client *c)
          379          }
          380          selmon->sel = c;
          381          drawbars();
          382 +        drawtabs();
          383  }
          384  
          385  /* there are some broken focus acquiring clients */
          386 @@ -868,6 +1013,19 @@ focusstack(const Arg *arg)
          387          }
          388  }
          389  
          390 +void
          391 +focuswin(const Arg* arg){
          392 +  int iwin = arg->i;
          393 +  Client* c = NULL;
          394 +  for(c = selmon->clients; c && (iwin || !ISVISIBLE(c)) ; c = c->next){
          395 +    if(ISVISIBLE(c)) --iwin;
          396 +  };
          397 +  if(c) {
          398 +    focus(c);
          399 +    restack(selmon);
          400 +  }
          401 +}
          402 +
          403  Atom
          404  getatomprop(Client *c, Atom prop)
          405  {
          406 @@ -1250,12 +1408,14 @@ propertynotify(XEvent *e)
          407                  case XA_WM_HINTS:
          408                          updatewmhints(c);
          409                          drawbars();
          410 +                        drawtabs();
          411                          break;
          412                  }
          413                  if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
          414                          updatetitle(c);
          415                          if (c == c->mon->sel)
          416                                  drawbar(c->mon);
          417 +                        drawtab(c->mon);
          418                  }
          419                  if (ev->atom == netatom[NetWMWindowType])
          420                          updatewindowtype(c);
          421 @@ -1369,6 +1529,7 @@ restack(Monitor *m)
          422          XWindowChanges wc;
          423  
          424          drawbar(m);
          425 +        drawtab(m);
          426          if (!m->sel)
          427                  return;
          428          if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
          429 @@ -1561,6 +1722,8 @@ setup(void)
          430          if (!drw->fontcount)
          431                  die("no fonts could be loaded.\n");
          432          bh = drw->fonts[0]->h + 2;
          433 +        th = bh;
          434 +
          435          updategeom();
          436          /* init atoms */
          437          wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
          438 @@ -1699,6 +1862,17 @@ togglebar(const Arg *arg)
          439  }
          440  
          441  void
          442 +tabmode(const Arg *arg)
          443 +{
          444 +        if(arg && arg->i >= 0)
          445 +                selmon->showtab = arg->ui % showtab_nmodes;
          446 +        else
          447 +                selmon->showtab = (selmon->showtab + 1 ) % showtab_nmodes;
          448 +        arrange(selmon);
          449 +}
          450 +
          451 +
          452 +void
          453  togglefloating(const Arg *arg)
          454  {
          455          if (!selmon->sel)
          456 @@ -1809,20 +1983,44 @@ updatebars(void)
          457                                            CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
          458                  XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
          459                  XMapRaised(dpy, m->barwin);
          460 +                m->tabwin = XCreateWindow(dpy, root, m->wx, m->ty, m->ww, th, 0, DefaultDepth(dpy, screen),
          461 +                                          CopyFromParent, DefaultVisual(dpy, screen),
          462 +                                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
          463 +                XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
          464 +                XMapRaised(dpy, m->tabwin);
          465          }
          466  }
          467  
          468  void
          469  updatebarpos(Monitor *m)
          470  {
          471 +        Client *c;
          472 +        int nvis = 0;
          473 +
          474          m->wy = m->my;
          475          m->wh = m->mh;
          476          if (m->showbar) {
          477                  m->wh -= bh;
          478                  m->by = m->topbar ? m->wy : m->wy + m->wh;
          479 -                m->wy = m->topbar ? m->wy + bh : m->wy;
          480 -        } else
          481 +                if ( m->topbar )
          482 +                        m->wy += bh;
          483 +        } else {
          484                  m->by = -bh;
          485 +        }
          486 +
          487 +        for(c = m->clients; c; c = c->next){
          488 +          if(ISVISIBLE(c)) ++nvis;
          489 +        }
          490 +
          491 +        if(m->showtab == showtab_always
          492 +           || ((m->showtab == showtab_auto) && (nvis > 1) && (m->lt[m->sellt]->arrange == monocle))){
          493 +                m->wh -= th;
          494 +                m->ty = m->toptab ? m->wy : m->wy + m->wh;
          495 +                if ( m->toptab )
          496 +                        m->wy += th;
          497 +        } else {
          498 +                m->ty = -th;
          499 +        }
          500  }
          501  
          502  void
          503 @@ -2063,7 +2261,7 @@ wintomon(Window w)
          504          if (w == root && getrootptr(&x, &y))
          505                  return recttomon(x, y, 1, 1);
          506          for (m = mons; m; m = m->next)
          507 -                if (w == m->barwin)
          508 +                if (w == m->barwin || w == m->tabwin)
          509                          return m;
          510          if ((c = wintoclient(w)))
          511                  return c->mon;