Skip to content

How to set up workspaces per monitor

Bakkeby edited this page Feb 27, 2024 · 3 revisions

By default dusk is configured with nine workspaces that can be moved or shared between monitors unless they are pinned to a particular monitor.

In general a keybinding like Super+<number> will either focus on the monitor that has that workspace or it will move the workspace to the current monitor. Each workspace is directly accessible via keybindings.

It may of course be preferable to have e.g. nine workspaces per monitor where Super+<number> brings you to the nth workspace on the current monitor - like how dwm has one tagset per monitor.

It is possible to achieve this in dusk with a few configuration changes and here is how.

The first step is to define workspaces for as many monitors that you have. Here we have two sets of nine workspaces for a dual monitor setup. Add nine more for a three monitor setup, and so on.

static const WorkspaceRule wsrules[] = {
	/*                                                                     ------------------------------- schemes ------------------------------- ------ icons ------
	   name,  monitor,  pinned,  layout,  mfact,  nmaster,  nstack,  gaps, default,          visible,          selected,         occupied,         def,   vac,  occ,  */
	{  "1",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "1",   "",   "[1]", },
	{  "2",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "2",   "",   "[2]", },
	{  "3",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "3",   "",   "[3]", },
	{  "4",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "4",   "",   "[4]", },
	{  "5",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "5",   "",   "[5]", },
	{  "6",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "6",   "",   "[6]", },
	{  "7",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "7",   "",   "[7]", },
	{  "8",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "8",   "",   "[8]", },
	{  "9",    0,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "9",   "",   "[9]", },
	{  "10",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "1",   "",   "[1]", },
	{  "11",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "2",   "",   "[2]", },
	{  "12",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "3",   "",   "[3]", },
	{  "13",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "4",   "",   "[4]", },
	{  "14",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "5",   "",   "[5]", },
	{  "15",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "6",   "",   "[6]", },
	{  "16",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "7",   "",   "[7]", },
	{  "17",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "8",   "",   "[8]", },
	{  "18",   1,       1,       0,       -1,    -1,       -1,      -1,    SchemeWsNorm,     SchemeWsVisible,  SchemeWsSel,      SchemeWsOcc,      "9",   "",   "[9]", },
};

There are a few things to note:

  • the workspace name continues with 10, 11, 12, etc. - this is because we want to have something unique to refer to for example in relation to client rules
  • all workspaces are explicitly assigned to a given monitor
  • all workspaces are pinned
  • in this case we keep the workspace icons consistent across monitors (optional)

The second step is to set up keybindings to make the general workspace actions work on a per monitor basis rather than across all monitors.

By default the keybindings are managed through the WSKEYS macro like this:

#define WSKEYS(MOD,KEY,NAME) \
	{ KeyPress,   MOD,                      KEY,      comboviewwsbyname,   {.v = NAME} }, \
	{ KeyPress,   MOD|Alt,                  KEY,      enablewsbyname,      {.v = NAME} }, \
	{ KeyPress,   MOD|Shift,                KEY,      movetowsbyname,      {.v = NAME} }, \
	{ KeyPress,   MOD|Ctrl,                 KEY,      sendtowsbyname,      {.v = NAME} }, \
	{ KeyPress,   MOD|Ctrl|Shift,           KEY,      movealltowsbyname,   {.v = NAME} }, \
	{ KeyPress,   MOD|Ctrl|Alt,             KEY,      moveallfromwsbyname, {.v = NAME} }, \
	{ KeyPress,   MOD|Ctrl|Alt|Shift,       KEY,      swapwsbyname,        {.v = NAME} }, \

which is used like this:

	WSKEYS(Super,                               XK_1,  "1")
	WSKEYS(Super,                               XK_2,  "2")
	WSKEYS(Super,                               XK_3,  "3")
	WSKEYS(Super,                               XK_4,  "4")
	WSKEYS(Super,                               XK_5,  "5")
	WSKEYS(Super,                               XK_6,  "6")
	WSKEYS(Super,                               XK_7,  "7")
	WSKEYS(Super,                               XK_8,  "8")
	WSKEYS(Super,                               XK_9,  "9")

We are going to change that macro to look up the workspace based on the nth index on the current monitor rather than via the name.

Here is what the new macro looks like:

#define WSKEYS(MOD,KEY,INDEX) \
	{ KeyPress,   MOD,                      KEY,      comboviewwsbyindex,   {.i = INDEX} }, \
	{ KeyPress,   MOD|Alt,                  KEY,      enablewsbyindex,      {.i = INDEX} }, \
	{ KeyPress,   MOD|Shift,                KEY,      movetowsbyindex,      {.i = INDEX} }, \
	{ KeyPress,   MOD|Ctrl,                 KEY,      sendtowsbyindex,      {.i = INDEX} }, \
	{ KeyPress,   MOD|Ctrl|Shift,           KEY,      movealltowsbyindex,   {.i = INDEX} }, \
	{ KeyPress,   MOD|Ctrl|Alt,             KEY,      moveallfromwsbyindex, {.i = INDEX} }, \
	{ KeyPress,   MOD|Ctrl|Alt|Shift,       KEY,      swapwsbyindex,        {.i = INDEX} }, \

and the way it is used:

	WSKEYS(Super,                               XK_1,  1)
	WSKEYS(Super,                               XK_2,  2)
	WSKEYS(Super,                               XK_3,  3)
	WSKEYS(Super,                               XK_4,  4)
	WSKEYS(Super,                               XK_5,  5)
	WSKEYS(Super,                               XK_6,  6)
	WSKEYS(Super,                               XK_7,  7)
	WSKEYS(Super,                               XK_8,  8)
	WSKEYS(Super,                               XK_9,  9)

Replace the existing macro and calls with the above and that's it for the keybindings.

The third step is optional but recommended.

Enable the workspaces_per_mon configuration option to change the behaviour of pinned workspaces so that workspaces end up being hidden rather than being distributed when a monitor is removed.

Without this you would end up with e.g. 18 workspaces on the remaining single monitor.

/* This determines what happens with pinned workspaces on a monitor when that monitor is removed.
 *   0 - the workspaces becomes unpinned and is moved to another monitor or
 *   1 - the workspace clients are moved to the selected workspace on the first monitor, but
 *       the workspace itself is hidden
 *
 * Non-pinned workspaces are always redistributed among the remaining monitors.
 */
static const int workspaces_per_mon = 1;

That's all. Recompile and give it a go.


Back to Guides.

Clone this wiki locally