Skip to content

tagicons

bakkeby edited this page Jul 1, 2024 · 4 revisions

This patch aims to combine and expand on the following patches:

Depending on your needs some configuration of tags will be required after patching. As such keybindings for related functions are commented out by default in the patch. It is up to you what you want out of this.

Because of the above the patch does produce compilation warnings due to unused code.

Changing the number of tags per monitor

In a traditional dwm the number of tags in use can be changed by changing the number of strings in the tags array.

With this patch the number of tags is controlled with the NUMTAGS macro in dwm.c, which is primarily used to differentiate between different icons per monitor.

Configuration

By default the patch comes with this setup which simply replicates the 1 through 9 tags that what comes with a bare dwm 6.3 build.

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { NULL },
};

Let's have a closer look at the different icon sets and features.

Tags per monitor

Tag icons can be defined on a per monitor basis and the position of the icons is derived based on the number of tags (NUMTAGS) and the monitor number (index). If the derived tag index is greater than the available icons then a modulus between the two is further applied.

Let's have look at a practical example:

static char *tagicons[][NUMTAGS*2] = {
	[IconsDefault]        = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { NULL },
};

In this case icons 1 through 9 is being used for monitor 0 while icons A through I are used for monitor 1.

How does that work exactly? Note the NUMTAGS*2 which doubles the number of icons. The icon for a tag is derived as tag index + monitor index * NUMTAGS. So for the third tag on the second monitor we end up with 2 + 1 * 9 which gives the icon of "C".

If a third monitor is used then that gives 2 + 2 * 9 (index 20). As that is greater than the number of icons (NUMTAGS*2) a modulus is applied: 20 % 18 = 2, giving the icon of "3". In other words the icons wraps around. A fourth monitor would then use icons A through I.

This mechanism comes into play again in the example below.

Repeating icons

Having identical tags can also make for an elegant look and feel. For example having all tags shown as bullet points gives a certain aesthetic.

In a normal dwm build that would be configured like this:

static const char *tags[] = { "•", "•", "•", "•", "•", "•", "•", "•", "•" };

With the tagicons patch this can be simplified to a single bullet point thanks to how icon indexes are derived and reduced:

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "•" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { NULL },
};

Similarly if you choose two tags A and B then tags will alternate between them.

If you want identical tags for all tags, but different tags per monitor, then you will have to explicitly define them all though.

Occupied tags

The IconsOccupied tag set will replace the default icons if there are clients occupating the given tag. This is a practical representation of the alttagsdecoration patch.

If IconsOccupied is NULL then that means that the feature is not to be used.

A practical example:

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" },
};

When dwm starts up tags 1 through 9 will be displayed. As soon as a client is opened on a tag then the occupied tag icon is being used instead. E.g. opening a client on tag 5 means that "<5>" will be shown instead of "5".

Hide vacant tags

To hide vacant (as in empty) tags simply set the icon text to be an empty string.

Taking advantage of the repeating icons logic we can set a single empty string to replicate the behavour of the hide vacant tags patch.

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" },
};

One benefit of doing it this way (using empty strings) is that you could choose to hide all tags except for special ones that you want to always display. Here is an example that hides all but the seventh, eight and ninth tag:

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "", "", "", "", "", "", "7", "8", "9" },
	[IconsVacant]         = { NULL },
	[IconsOccupied]       = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" },
};

If a client exists on tag 3 then "<3>" will be shown as per the occupied tags feature.

This brings us to an exception; vacant tags.

Vacant tags

The IconsVacant tag set is a bit of an exception and is only used when:

  • the default icon is hidden (i.e. being an empty string) and
  • the selected tag is not occupied

In other words this is only used when you explicitly view a tag that has no clients on it.

An example configuration may look like:

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "" },
	[IconsVacant]         = { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
	[IconsOccupied]       = { "<1>", "<2>", "<3>", "<4>", "<5>", "<6>", "<7>", "<8>", "<9>" },
};

Here all tags are hidden by default, but show "1", "2", etc. when selected unless they have clients on them.

Alternative tags

Icon sets are defined by an enum in dwm.c:

enum {
	IconsDefault,
	IconsVacant,
	IconsOccupied,
	IconsLast
}; /* icon sets */

As with colour schemes you can add your own sets here and as many as you want. Here is an example setup:

static char *tagicons[][NUMTAGS] = {
	[IconsDefault]        = { "1", "2", "3", "4", "5", "6", "7", "8", "9" },
	[IconsBullets]        = { "•" },
	[IconsLetters]        = { "A", "B", "C", "D", "E", "F", "G", "H", "I" },
	[IconsText]           = { "mail", "chat", "office", "gimp", "prog", "misc", "play", "mon", "web" },
	[IconsSubscript]      = { " ₁", " ₂", " ₃", " ₄", " ₅", " ₆", " ₇", " ₈", " ₉"
	[IconsVacant]         = { "•₁", "•₂", "•₃", "•₄", "•₅", "•₆", "•₇", "•₈", "•₉" },
	[IconsOccupied]       = { "◉₁", "☢₂", "❖₃", "⚉₄", "♻₅", "⌬₆", "♹₇", "✇₈", "☉₉" },
};

You can then cycle through the various icon sets with the cycleiconset function by using the mouse scroll wheel on the tags (provided that the button binding is set of course). Alternatively you can add keybindings to set specific icon sets if you prefer.

This extends the alternativetags patch by allowing you to have multiple alternative tags rather than just the one.

It should be noted though that with the implementation as-is the IconsOccupied icon set will always override the selected icon set if the tag is occupied by clients. Being able to define occupied and vacant icon sets on a per icon set basis was considered, but was deemed to add too much complexity for a feature that most people would likey not want. One likely solution could be to use variables that point to the sets that defines vacant and occupied icons, and allow these to be changed during runtime.

Different number of tags per monitor

Given the way tag masks work it doesn't make much sense to try to configure a different number of tags per monitor - at best one will end up just blocking the use of certain tags in the view, toggleview, tag and toggletag functions.

The easiest solution here is to just hide the tags that you do not want shown.

To do this properly one should probably look into implementing actual workspaces (a monumental task) or fall back to using the single tagset patch which has a few issues on its own.

Compatibility with other patches

The original tags array has been left in the code for compatibility reasons because many other patches rely on LENGTH(tags) without necessarily using the tag text.

For all intensive purposes LENGTH(tags) can be safely replaced with NUMTAGS in such cases.

There are a few patches that need additional integration steps:

ewmhtags - the setdesktopnames function explicitly passes the tags array to Xutf8TextListToTextProperty, you may want to replace it with something like this:

void
setdesktopnames(void)
{
	int i;
	XTextProperty text;
	char *tags[NUMTAGS];
	for (i = 0; i < NUMTAGS; i++)
		tags[i] = tagicon(selmon, i);
	Xutf8TextListToTextProperty(dpy, tags, NUMTAGS, XUTF8StringStyle, &text);
	XSetTextProperty(dpy, root, &text, netatom[NetDesktopNames]);
}

ipc - in the run function of dwm.c you may want to replace

			} else if (ipc_is_client_registered(event_fd)){
				if (ipc_handle_client_epoll_event(events + i, mons, &lastselmon, selmon,
							tags, LENGTH(tags), layouts, LENGTH(layouts)) < 0) {
					fprintf(stderr, "Error handling IPC event on fd %d\n", event_fd);
				}
			} else {

with

			} else if (ipc_is_client_registered(event_fd)){
				char *tags[NUMTAGS];
				for (int t = 0; t < NUMTAGS; t++)
					tags[t] = tagicon(selmon, t);
				if (ipc_handle_client_epoll_event(events + i, mons, &lastselmon, selmon,
							tags, NUMTAGS, layouts, LENGTH(layouts)) < 0) {
					fprintf(stderr, "Error handling IPC event on fd %d\n", event_fd);
				}
			} else {

Download

Clone this wiki locally