Optimizing the Desktop Environment: Top Menu Bar
Tags: productivity, desktop, unix, linux
Published: 2022-07-22 05:00:00

## Purpose

- To question commonly adopted practices


## Top Menu Bar

If you are using only a laptop screen, you are already constrained with a limited screen real estate. You would want to put every pixel to some good use. The top menu bar consumes a permanent space. If you think about it, auto-hide and full-screen do not actually let you view the top menu bar contents. So, if you need to view your network status when you are in a full-screen app/mode, you need to get out of it.

Typical contents of a top menu bar are:
- date and time
- network status
- battery status
- volume control 
- some apps prominently controlled with a tray icon

Think about the percentage of the total time you spend on your machine that you devote to using the above items! 

Lastly, is the viewing experience optimal? Do you really like (unknowingly) straining your eyes to watch a 10px date/time text placed in a corner of the screen? Or would you rather have it in a larger font and in the center of the screen?

## What do I do?

I prefer to use my keyboard.
I disabled my top menu bar completely.
I execute a shell script which collects all the info (like battery status, network status etc). 
I have a notification configured to display this info at the center of the screen every 15 minutes. 
Additionally, I have a keyboard shortcut to view the notification on demand. 
Regarding apps prominently controlled using the tray icon, I had only *safeeyes* which thankfully has a cli. If anything else comes up, I am sure linux based distros/apps are flexible enough to help me deal with it.

![Notification](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68fu91dcalvbvwgv5k5g.png)


To get this working I also enjoyed setting up a daemon using systemd timers, configuring dunst and learning about the shell commands/applications which communicate with device hardware for their status. That's plenty of unix fun to push me for a post!


## Code

I use Arch linux but the below scripts could still help or get you started if you are using some other linux based distros.

### nodejs script

You may have to edit it to suit your needs. For example, instead of **nmcli**, you could  use your existing network utility and extract its status output. Same for Xmonad (**xprop**).

```
const util = require("util");
const stdExec = require("child_process").exec;
const exec = util.promisify(stdExec);

const appNameForDunst = "CombinedNotification";
const executeScript = async (shellCommand) => {
  return await exec(shellCommand);
};

const notifySendCommand = async (text) =>
  await executeScript(
    `notify-send -t 5000 -a ${appNameForDunst} "Status\n" "${text}"`
  );

const commands = [
  {
    title: "Date",
    command: "date '+ %a %d %b %y'",
    process: "return output",
    color: "orange",
  },
  {
    title: "Time",
    command: "date '+ %I:%M'",
    process: "return output",
    color: "yellow",
  },
  {
    title: "Battery",
    command: "acpi -b",
    process:
      'let commaSplit = output.split(","); let chargingStatus = commaSplit[0].split(":")[1]; return `${chargingStatus} ${commaSplit[1]}`',
    color: "silver",
  },
  {
    title: "Network",
    command: "nmcli -t -f NAME connection show --active",
    process: "return output",
    color: "cyan",
  },
  {
    title: "Workspace",
    command: "xprop -root _NET_CURRENT_DESKTOP",
    process:
      'return ["view", "dev", "servers", "config", "notes", "work"][output.trim().substr(-1)]',
    color: "violet",
  },
  {
    title: "Window Count",
    command: "xprop -root _NET_CLIENT_LIST_STACKING",
    process: 'return output.split("#").join("").split(",").length',
    color: "white",
  },
];

let displayText = "";

const handler = async (command, index) => {
  const result = await executeScript(command.command);
  const processOutput = new Function("output", command.process);
  const lineEndingChar =
    ["Date", "Time"].includes(command.title) || index == commands.length
      ? ""
      : "\n";
  displayText =
    displayText +
    `<span>${command.title}:</span> <span color='${
      command.color
    }'>${processOutput(result.stdout)}</span>${lineEndingChar}`;
  console.log(displayText);
};

const main = async () => {
  try {
    await Promise.allSettled(commands.map(handler));
    notifySendCommand(displayText);
  } catch (error) {
    console.log(error);
  }
};

main();
```

### systemd scripts

I placed these in *~/.config/systemd/user*

notification.service
```
[Unit]
Description=Show notification service
Wants=notification.timer

[Service]
Type=simple
ExecStart=/usr/bin/node /home/<username>/<path-to-nodejs-script>
Environment="DISPLAY=:0" "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus" "XAUTHORITY=/home/<username>/.Xauthority"
```
notification.timer
```
[Unit]
Description=Show notification timer

[Timer]
# The Unit item is not necessary as long as the name before the .service extension is same
Unit=notification.service
OnBootSec=2m
OnUnitActiveSec=15m

[Install]
WantedBy=timers.target
```