Work with Polybar in Emacs X Window Manager, Part 1: Getting Started

For the past six months, I’ve used the Emacs X Window Manager daily as my main window manager and it’s been a great tool. It’s enabled me to move from using a disparate set of tools to now having an Integrated Computing Environment. It’s true: Emacs really does make a great operating system.

Starting and Stopping Polybar

Like many, I’ve adopted a lot of the configuration used by David Wilson of System Crafters. Part of that configuration includes his efs/start-panel and efs/kill-panel functions. These functions start a Polybar shell command from within Emacs while making sure to kill any existing processes before spawning a new instance. It does this by capturing a reference to the process that later can be passed to the built-in kill-process function.

The Problem

Unfortunately, I run a multi-monitor setup, and David’s script that I borrowed only launches polybar on the main screen. As a temporary workaround, I continued to use the bash script I call from within my old bspwm configuration to launch polybar. This didn’t feel very Emacs Zen-like, so I set out to devise a better way.

My Solution

I start by setting up an empty variable that will hold references to each running polybar.


(defvar bp/polybar-processes nil 
  "A list of running polybar processes. So that we can kill them later. 👿")

In the next section, I query for a list of attached monitors, this type via polybar itself. I could use xrandr for this, but this output is easier to parse. Also, it’s one less dependency to rely upon from within a single script. Using cut I split the input by the delimiter “:”, and take the first fragment, remove extraneous new lines. Finally, break the monitor names into their own entries.


(defun bp/get-monitors-list () 
  "Get a list of the currently connected monitors. Requires polybar, instead of relying on xrandr, though you probably want it installed too."
  (split-string 
    (substring 
      (shell-command-to-string "polybar -m | cut -d: -f 1") 0 -1) "\n"))

As defined, the bp/kill-panel function will iterate over the list of running polybar processes and stop them in succession, and finally, resetting the process list to nil.


(defun bp/kill-panel () 
  "Stop any running polybar processes"
  (interactive)
  (let ((process-list bp/polybar-processes)) 
    (dolist 
      (p process-list) 
      (kill-process p))) 
    (setq bp/polybar-processes nil))

As its first order of business, the bp/start-panel function calls the bp/kill-panel function to clear out any existing running processes. It then calls bp/get-monitors-list, mapping over our list monitors and interpolating their names into the shell command to start polybar.


(defvar bp/polybar-config-location "~/.doom.d/exwm/polybar.config.ini" 
  "The customized location of your polybar config.ini. You'll most certainly want to customize this value. ")

(defun bp/start-panel () 
  "Start polybar on each connected monitor"
   (interactive)
   (bp/kill-panel) 
  (setq bp/polybar-processes 
    (mapcar (lambda (monitor) 
      (start-process-shell-command "polybar" nil 
        (format "MONITOR=%s polybar -c %s --reload main" monitor bp/polybar-config-location))) 
      (bp/get-monitors-list))))

With these in place, I can now interactively show and hide the polybar as needed from within Emacs.

Stay tuned for part two of this series, where we’ll set up workspace indicators on polybar.