sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

dwm-tab-v2b-56a31dc.diff (15708B)


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