<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Blog entries tagged linux :: mwop.net</title>
  <updated>2026-05-04T08:35:08-05:00</updated>
  <generator uri="https://getlaminas.org" version="2">Laminas_Feed_Writer</generator>
  <link rel="alternate" type="text/html" href="https://mwop.net/blog/tag/linux"/>
  <link rel="self" type="application/atom+xml" href="https://mwop.net/blog/tag/linux/atom.xml"/>
  <id>https://mwop.net/blog/tag/linux</id>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Determining if a reboot is required on Linux]]></title>
    <published>2026-05-04T08:35:08-05:00</published>
    <updated>2026-05-04T08:35:08-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2026-05-04-reboot-required.html"/>
    <id>https://mwop.net/blog/2026-05-04-reboot-required.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I've been using the construct <xhtml:code>sudo run-parts
/etc/update-motd.d/</xhtml:code> to determine if recent system updates
required a reboot.</xhtml:p>
<xhtml:p>Today I learned I can simply check for the existence of the file
<xhtml:code>/var/run/reboot-required</xhtml:code>.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2026-05-04-reboot-required.html">Determining
if a reboot is required on Linux</xhtml:a> was originally published
<xhtml:time class="dt-published" datetime="2026-05-04T08:35:08-05:00">4
May 2026</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a>
by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Headless boot for Beelink EQ14]]></title>
    <published>2026-02-24T10:09:08-06:00</published>
    <updated>2026-03-23T14:06:08-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2026-02-24-headless-boot-beelink-eq14.html"/>
    <id>https://mwop.net/blog/2026-02-24-headless-boot-beelink-eq14.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>A few months back, I purchased a Beelink EQ14 mini server for my
homelab. One thing that has frustrated me has been that rebooting
or even powering on... has required I have either my keyboard or my
monitor plugged in (I haven't been able to determine exactly which
one yet). This has made me reluctant to reboot, as I then have to
switch my keyboard from my workstation over to the server, and plug
the HDMI from one of my monitors into it. I hate it.</xhtml:p>
<xhtml:p>In browsing a number of forums, I saw one recommendation to
switch the BIOS to use FastBoot.</xhtml:p>
<xhtml:p><xhtml:del>Can confirm, works!</xhtml:del></xhtml:p>
<xhtml:h4>Update</xhtml:h4>
<xhtml:p>While FastBoot helps, it doesn't entirely fix the issues. What I
discovered was that the server was requiring my keyboard in order
to boot; I tested this by rebooting the machine without cables, and
plugging in only the keyboard.</xhtml:p>
<xhtml:p>Unfortunately, there are no BIOS settings to override this
behavior. I dug up an old USB dongle for a presentation pointer,
plugged that in, and voilá — it boots now. I suspect any USB HID
dongle will likely work.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2026-02-24-headless-boot-beelink-eq14.html">Headless
boot for Beelink EQ14</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2026-02-24T10:09:08-06:00">24 February
2026</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[When docker buildx cannot resolve your container registry]]></title>
    <published>2026-02-11T09:49:31-06:00</published>
    <updated>2026-02-11T09:49:31-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2026-02-11-docker-daemon-resolve-conf.html"/>
    <id>https://mwop.net/blog/2026-02-11-docker-daemon-resolve-conf.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I had an odd situation today when building and pushing a
container image to a registry.</xhtml:p>
<xhtml:p>I have a private registry in my homelab. I have an internal DNS
server that can resolve it when in my home network, and my machines
in my home network all use that internal DNS server. Great,
fantastic, just works.</xhtml:p>
<xhtml:p>Except for some reason, when running a <xhtml:code>docker buildx build
--push</xhtml:code> operation, it was unable to resolve the internal
name... because it was using the wrong DNS server. It was trying to
resolve via the 8.8.8.8 DNS server. The Google DNS servers. Which
I've not even configured as an upstream in my recursive DNS server
on the local network.</xhtml:p>
<xhtml:p>My guess is that the Docker daemon falls back to Google DNS
servers if the system DNS cannot be reached at any point. Clearly,
it then never tests to see if the system DNS has become reachable
again, so it gets stuck there.</xhtml:p>
<xhtml:p>The solution? Restart the docker service:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">sudo systemctl restart docker
</xhtml:code></xhtml:pre>
<xhtml:p>Once I did that, it resolved using the system DNS next time.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2026-02-11-docker-daemon-resolve-conf.html">When
docker buildx cannot resolve your container registry</xhtml:a> was
originally published <xhtml:time class="dt-published" datetime="2026-02-11T09:49:31-06:00">11 February 2026</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Removing a filename containing a null byte or binary character on Linux]]></title>
    <published>2026-02-06T11:01:27-06:00</published>
    <updated>2026-02-06T11:01:27-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2026-02-06-linux-null-byte-filename-removal.html"/>
    <id>https://mwop.net/blog/2026-02-06-linux-null-byte-filename-removal.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>Somehow, I got a file in my tree that started with a null byte.
I only discovered it because <xhtml:code>git status</xhtml:code> was noting it
wasn't added to my branch, and wanted to know if I wanted to add
it.</xhtml:p>
<xhtml:p>No, no, I did not. I wanted to delete it.</xhtml:p>
<xhtml:p>But, of course, you cannot reference such a file by name, so I
had to learn a few tricks.</xhtml:p>
<xhtml:p>First off, you can list names with binary bytes using:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">/bin/ls -lb
</xhtml:code></xhtml:pre>
<xhtml:p>But even better, you can get the inode if you use:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">/bin/ls -li
</xhtml:code></xhtml:pre>
<xhtml:p>When you do, the inode is in the first column of the list for
each file.</xhtml:p>
<xhtml:p>Once you know that, you can delete the file, using find:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">find . -maxdepth 1 -inum &lt;inode_id&gt; -<xhtml:span class="hljs-built_in">exec</xhtml:span> rm -i -- {} +
</xhtml:code></xhtml:pre>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2026-02-06-linux-null-byte-filename-removal.html">
Removing a filename containing a null byte or binary character on
Linux</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2026-02-06T11:01:27-06:00">6 February 2026</xhtml:time> on
<xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[From zsh to fish]]></title>
    <published>2025-11-17T17:00:10-06:00</published>
    <updated>2025-11-17T17:00:10-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2025-11-17-from-zsh-to-fish.html"/>
    <id>https://mwop.net/blog/2025-11-17-from-zsh-to-fish.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I'm a longtime zsh user. A colleague introduced me to it in
2009, and I was an instant convert, if nothing else than for
directory aliases and simpler <xhtml:code>$PATH</xhtml:code> management. Within
a couple of years, I discovered <xhtml:a href="https://ohmyz.sh">oh-my-zsh</xhtml:a>, which put my shell on steroids,
giving me a ton of completion capabilities, better prompts, and
more.</xhtml:p>
<xhtml:p>But a few years ago, I started noticing that my shell load times
were getting worse and worse. At that time, I discovered I could
easily switch to vanilla zsh with <xhtml:a href="https://zplug.github.io">zplug</xhtml:a> managing a small number of
plugins I used (nvm, fzf, and a few others). I also discovered
<xhtml:a href="https://startship.rs">starship</xhtml:a>, which gave me more
prompt options, with faster startup times.</xhtml:p>
<xhtml:p>And yet...</xhtml:p>
<xhtml:p>I kept reading <xhtml:a href="https://jvns.ca">Julia Evans</xhtml:a>
recommending <xhtml:a href="https://fishshell.com">fish</xhtml:a>. She often
would note that fish just does things that other shells need
plugins or customization for: decent tab completion, better history
capabilities, etc.</xhtml:p>
<xhtml:p>So I took the plunge finally in the past couple weeks to give
fish a try.</xhtml:p>
<xhtml:h2>Configuration</xhtml:h2>
<xhtml:p>First off, the switch more than halved the amount of
configuration I needed to have an equivalent setup. I was able to
remove a ton of configuration I had in zsh around history
management, autocompletion, and overrides.</xhtml:p>
<xhtml:p>As I noted, with zsh, I was using zplug, and I had a half-dozen
plugins. With fish, initially I had no plugins, but in order to get
<xhtml:a href="https://github.com/nvm-sh/nvm">nvm</xhtml:a> running, I needed
to install <xhtml:a href="https://github.com/jorgebucaran/fisher">fisher</xhtml:a>, the de facto
fish plugin manager. But that's literally the only plugin I'm now
using.</xhtml:p>
<xhtml:p>I continued to use starship and fzf, which meant two lines of
configuration in my fish configuration, and no changes
otherwise.</xhtml:p>
<xhtml:p>And there's no lag whatsoever when starting up a shell. With
zsh, even with my minimal config, I would sometimes wait a second
or two for a shell to spawn. With fish, no wait.</xhtml:p>
<xhtml:h2>Discoveries</xhtml:h2>
<xhtml:p>One thing I've kept from oh-my-zsh is a utility called
<xhtml:code>take</xhtml:code>, which does the following:</xhtml:p>
<xhtml:ul>
<xhtml:li>If given a directory name, it creates it, and then enters
it</xhtml:li>
<xhtml:li>If given an archive file, it unarchives it into a directory,
and then enters that directory</xhtml:li>
<xhtml:li>If given a git repository name, it clones it, and then enters
that directory</xhtml:li>
</xhtml:ul>
<xhtml:p>When porting this to fish, I discovered some really cool
features of that shell.</xhtml:p>
<xhtml:p>First, fish will automatically autoload functions from the
<xhtml:code>functions/</xhtml:code> subdirectory of your fish configuration. So
if you name the function the same as the file (e.g.,
<xhtml:code>functions/take.fish</xhtml:code>), it will load it <xhtml:em>on
demand</xhtml:em>. This is a nice performance improvement over loading
<xhtml:em>everything</xhtml:em>.</xhtml:p>
<xhtml:p>Second, fish uses a standard syntax for any block statement.
Instead of sometimes needing braces, sometimes needing a keyword
(which generally varies BASED on the block type - e.g.
<xhtml:code>fi</xhtml:code> to end a conditional, <xhtml:code>done</xhtml:code> to end a
loop), all blocks use an <xhtml:code>end</xhtml:code> keyword. This makes it
far simpler to remember and less prone to errors.</xhtml:p>
<xhtml:p>Third, when defining a function, you can specify variable names
to which to capture arguments. This is far easier to visually parse
and use than standard posix shells, where you use positional
parameters. As an example:</xhtml:p>
<xhtml:pre><xhtml:code class="language-shell hljs shell" data-lang="shell">function takedir -a newpath
    mkdir -p $newpath &amp;&amp; cd $newpath
end
</xhtml:code></xhtml:pre>
<xhtml:p>Fourth, while you <xhtml:em>can</xhtml:em> use the notation
<xhtml:code>varname=value</xhtml:code> to define variables, there's a better
built-in, the <xhtml:code>set</xhtml:code> directive, which can:</xhtml:p>
<xhtml:ul>
<xhtml:li>Define block-local (<xhtml:code>set -l</xhtml:code>) and function-local
(<xhtml:code>set -f</xhtml:code>) variables</xhtml:li>
<xhtml:li>Define globally-available variables (<xhtml:code>set -g</xhtml:code>)</xhtml:li>
<xhtml:li>Define environment variables (<xhtml:code>set -x</xhtml:code>, for
e<xhtml:strong>x</xhtml:strong>port) that persist to child shells</xhtml:li>
</xhtml:ul>
<xhtml:p>Using this, it's far easier both to ensure that a variable is
scoped correctly, as well as to reason about the scope of a given
variable. And those captured arguments I mentioned? Automatically
scoped to the function, so they won't bleed outside of it.</xhtml:p>
<xhtml:p>(There's also a "univeral" flag, <xhtml:code>-u</xhtml:code>, which will not
only set it in the current shell, at the globally available level,
but make it available across any other instances, and persist it
for future invocations. This seems dangerous, though!)</xhtml:p>
<xhtml:p>The combination of these meant that the <xhtml:code>take</xhtml:code>
declaration took fewer lines of code, was easier to understand, and
less likely to bleed state. I'll take it!</xhtml:p>
<xhtml:h2>But will I stick with it?</xhtml:h2>
<xhtml:p>I think so. I even put it on some servers I maintain, and it's
instantly given me more and better functionality than the default
shell available on each, which makes being on those servers more
comfortable. Having less configuration is something I've been
keeping an eye on, as more configuration means it's harder to
reason about how things work, and more likely to break or fail in
interesting ways when updating or upgrading.</xhtml:p>
<xhtml:p>I'll still need to keep my bash chops; provisioning scripts for
containers and VMs generally have to depend on this lowest common
denominator. However, having a useful out-of-the-box shell for my
workstation and servers that's easy to script? I'll take it.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2025-11-17-from-zsh-to-fish.html">From zsh
to fish</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2025-11-17T17:00:10-06:00">17 November 2025</xhtml:time> on
<xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Linux desktop files and xdg-open]]></title>
    <published>2025-11-03T13:39:35-06:00</published>
    <updated>2025-11-03T13:39:35-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2025-11-03-desktop-xdg-open.html"/>
    <id>https://mwop.net/blog/2025-11-03-desktop-xdg-open.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I've been using Linux on the desktop for more than 25 years now.
While I don't put icons on my desktop any longer (and haven't for
probably around 15 years), I <xhtml:em>do</xhtml:em> use the gnome-shell
launcher to quickly open programs, and this utilizes desktop
files.</xhtml:p>
<xhtml:p>Recently, I wanted to create launchers for different Obsidian
vaults. Obsidian provides a URL schema for this:
<xhtml:code>obsidian://open?vault=VaultName</xhtml:code>. The application
registers the schema handler with the system, so this should open,
but evidently you can no longer use "Type=Link" in your desktop
files.</xhtml:p>
<xhtml:p>What I found:</xhtml:p>
<xhtml:ul>
<xhtml:li>You MUST have a "Version=1.0" line; gnome-shell just ignored
any of my desktop files that omitted it.</xhtml:li>
<xhtml:li>You can use <xhtml:code>xdg-open</xhtml:code> in your <xhtml:code>Exec</xhtml:code>
line to open the URL.</xhtml:li>
</xhtml:ul>
<xhtml:pre><xhtml:code class="language-ini hljs ini" data-lang="ini"><xhtml:span class="hljs-section">[Desktop Entry]</xhtml:span>
<xhtml:span class="hljs-attr">Version</xhtml:span>=<xhtml:span class="hljs-number">1.0</xhtml:span>
<xhtml:span class="hljs-attr">Name</xhtml:span>=Notes
<xhtml:span class="hljs-attr">Icon</xhtml:span>=/usr/share/icons/hicolor/<xhtml:span class="hljs-number">256</xhtml:span>x256/apps/obsidian.png
<xhtml:span class="hljs-attr">Comment</xhtml:span>=My Obsidian vault for notes
<xhtml:span class="hljs-attr">Categories</xhtml:span>=<xhtml:span class="hljs-literal">Off</xhtml:span>ice<xhtml:span class="hljs-comment">;ProjectManagement;</xhtml:span>
<xhtml:span class="hljs-attr">Type</xhtml:span>=Application
<xhtml:span class="hljs-attr">Exec</xhtml:span>=xdg-open <xhtml:span class="hljs-string">"obsidian://open?vault=notes"</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2025-11-03-desktop-xdg-open.html">Linux
desktop files and xdg-open</xhtml:a> was originally published
<xhtml:time class="dt-published" datetime="2025-11-03T13:39:35-06:00">3
November 2025</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Disabling the Zoom mini window on Linux]]></title>
    <published>2025-09-23T09:20:27-05:00</published>
    <updated>2025-12-01T13:38:27-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2025-09-23-zoom-disabling-mini-window.html"/>
    <id>https://mwop.net/blog/2025-09-23-zoom-disabling-mini-window.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>Zoom used to have a strange behavior, one I'm sure they thought
would be useful, but in reality was infuriating: if you moved
between virtual workspaces, Zoom would minimize to a thumbnail
window that followed you around to workspaces.</xhtml:p>
<xhtml:p>At some point, it went away, thankfully... but after a recent
release, it turned back on, and it's been a huge pain for me.
There's a bug in that the mini window follows me to the initial
virtual workspace, but then doesn't follow around from there,
requiring me to use the workspace tools to move it to the workspace
I want, and then re-maximize it, only to have to do the whole thing
again if I switch screens.</xhtml:p>
<xhtml:p>The solution turns out to be relatively simple, if incredibly
unintuitive.</xhtml:p>
<xhtml:p>Zoom does NOT have a GUI setting for this, for some reason. But
evidently it creates the file
<xhtml:code>$HOME/.config/zoomus.conf</xhtml:code>, which is where all
configuration is stored.</xhtml:p>
<xhtml:p>This file is in <xhtml:a href="https://en.wikipedia.org/wiki/INI_file">INI format</xhtml:a>. Find the
entry <xhtml:code>enableMiniWindow</xhtml:code>, and set it to false:</xhtml:p>
<xhtml:pre><xhtml:code class="language-ini hljs ini" data-lang="ini"><xhtml:span class="hljs-attr">enableMiniWindow</xhtml:span>=<xhtml:span class="hljs-literal">false</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Restart Zoom, and you're set.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2025-09-23-zoom-disabling-mini-window.html">Disabling
the Zoom mini window on Linux</xhtml:a> was originally published
<xhtml:time class="dt-published" datetime="2025-09-23T09:20:27-05:00">23
September 2025</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Wezterm GUI Notifications]]></title>
    <published>2024-10-21T17:01:18-05:00</published>
    <updated>2024-10-21T17:01:18-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2024-10-21-wezterm-notify-send.html"/>
    <id>https://mwop.net/blog/2024-10-21-wezterm-notify-send.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p><xhtml:a href="https://wezfurlong.org/wezterm/index.html">Wezterm</xhtml:a>
has a utility for raising GUI system notifications, <xhtml:a href="https://wezfurlong.org/wezterm/config/lua/window/toast_notification.html">
window:toast_notification()</xhtml:a>, which is a handy way to bring
notifications to you that you might otherwise miss if the window is
hidden or if a given tab is inactive.</xhtml:p>
<xhtml:p>However, on Linux, it's a far from ideal tool, at least under
gnome-shell. (I don't know how it does on KDE or other desktop
environments.) It raises the notification, but the notification
never times out, even if you provide a timeout value (fourth
argument to the function). This means that you have to manually
dismiss the notification, which can be annoying, particularly if
the notifications happen regularly.</xhtml:p>
<xhtml:p>So, I worked up my own utility.</xhtml:p>
<xhtml:h2>notify.send</xhtml:h2>
<xhtml:p>Since Wezterm uses Lua for configuration, configuration actually
also acts as an extension mechanism. The primary wezterm Lua module
itself provides a <xhtml:code>run_child_process</xhtml:code> function for
spawning a system process, which allows me to call on the system
<xhtml:code>notify-send</xhtml:code> utility.</xhtml:p>
<xhtml:p>As such, I wrote up the following Lua module:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"># File: notify.lua
<xhtml:span class="hljs-keyword">local</xhtml:span> wezterm = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'wezterm'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> module  = {}

<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">has_value</xhtml:span> <xhtml:span class="hljs-params">(tab, val)</xhtml:span></xhtml:span>
    <xhtml:span class="hljs-keyword">for</xhtml:span> index, value <xhtml:span class="hljs-keyword">in</xhtml:span> <xhtml:span class="hljs-built_in">ipairs</xhtml:span>(tab) <xhtml:span class="hljs-keyword">do</xhtml:span> <xhtml:span class="hljs-comment">-- luacheck: ignore 213</xhtml:span>
        <xhtml:span class="hljs-keyword">if</xhtml:span> value == val <xhtml:span class="hljs-keyword">then</xhtml:span>
            <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-literal">true</xhtml:span>
        <xhtml:span class="hljs-keyword">end</xhtml:span>
    <xhtml:span class="hljs-keyword">end</xhtml:span>

    <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-keyword">end</xhtml:span>

<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">notify</xhtml:span> <xhtml:span class="hljs-params">(subject, msg, urgency)</xhtml:span></xhtml:span>
    <xhtml:span class="hljs-keyword">local</xhtml:span> allowed_urgency = { <xhtml:span class="hljs-string">'low'</xhtml:span>, <xhtml:span class="hljs-string">'normal'</xhtml:span>, <xhtml:span class="hljs-string">'critical'</xhtml:span> }
    urgency = urgency <xhtml:span class="hljs-keyword">or</xhtml:span> <xhtml:span class="hljs-string">'normal'</xhtml:span>
    <xhtml:span class="hljs-keyword">if</xhtml:span> <xhtml:span class="hljs-keyword">not</xhtml:span> has_value(allowed_urgency, urgency) <xhtml:span class="hljs-keyword">then</xhtml:span>
        urgency = <xhtml:span class="hljs-string">'normal'</xhtml:span>
    <xhtml:span class="hljs-keyword">end</xhtml:span>

    wezterm.run_child_process {
        <xhtml:span class="hljs-string">'notify-send'</xhtml:span>,
        <xhtml:span class="hljs-string">'-i'</xhtml:span>,
        <xhtml:span class="hljs-string">'org.wezfurlong.wezterm'</xhtml:span>,
        <xhtml:span class="hljs-string">'-a'</xhtml:span>,
        <xhtml:span class="hljs-string">'wezterm'</xhtml:span>,
        <xhtml:span class="hljs-string">'-u'</xhtml:span>,
        urgency,
        subject,
        msg
    }
<xhtml:span class="hljs-keyword">end</xhtml:span>

module.send = notify

<xhtml:span class="hljs-keyword">return</xhtml:span> module
</xhtml:code></xhtml:pre>
<xhtml:p>Within other configuration, when I want to send GUI
notifications, I can do the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-keyword">local</xhtml:span> notify = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'./notify'</xhtml:span>

notify.send(<xhtml:span class="hljs-string">'Subject Line'</xhtml:span>, <xhtml:span class="hljs-string">'This is the full message'</xhtml:span>, <xhtml:span class="hljs-string">'low'</xhtml:span>)
</xhtml:code></xhtml:pre>
<xhtml:p>Some notes on usage:</xhtml:p>
<xhtml:ul>
<xhtml:li>The <xhtml:code>urgency</xhtml:code> argument is one of 'low', 'normal', or
'critical', and defaults to 'normal'. These correspond to the same
<xhtml:code>--urgency</xhtml:code> option of <xhtml:code>notify-send</xhtml:code>, with the
following behavior:
<xhtml:ul>
<xhtml:li>'low' urgency messages are collected in the notification panel,
but not displayed.</xhtml:li>
<xhtml:li>'normal' urgency messages display until the system timeout for
notifications is met. (For me, that's 3 seconds.) After that, it
disappears into the notification panel.</xhtml:li>
<xhtml:li>'critical' urgency messages require manual dismissal.</xhtml:li>
</xhtml:ul>
</xhtml:li>
<xhtml:li>The <xhtml:code>subject</xhtml:code> argument is used for the notification
subject; it's what you see without expanding the notification.</xhtml:li>
<xhtml:li>The <xhtml:code>msg</xhtml:code> argument is the full detail you want in
the notification, and is shown when you expand the
notification.</xhtml:li>
</xhtml:ul>
<xhtml:p><xhtml:code>notify-send</xhtml:code> has a variety of other flags which can
control things like timeout, whether or not the message is
transient (i.e., will never be displayed in the notification
panel), application category, etc. I've found for myself that just
these three items are sufficient for probably 99% of any uses cases
I'll need.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2024-10-21-wezterm-notify-send.html">Wezterm
GUI Notifications</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2024-10-21T17:01:18-05:00">21 October
2024</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Wezterm Dropdown in Gnome]]></title>
    <published>2024-09-17T15:37:01-05:00</published>
    <updated>2024-09-17T15:37:01-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2024-09-17-wezterm-dropdown.html"/>
    <id>https://mwop.net/blog/2024-09-17-wezterm-dropdown.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>In <xhtml:a href="/blog/2024-07-04-how-i-use-wezterm.html">a previous
article</xhtml:a>, I detailed how I use Wezterm. One goal I had when
switching to Wezterm to was to ensure I was able to continue using
a dropdown terminal, and in that article, I detailed using the
<xhtml:code>tdrop</xhtml:code> utility to implement this... but with the caveat
that it didn't work well under the Wayland environment.</xhtml:p>
<xhtml:p>Well, I've now found a better solution.</xhtml:p>
<xhtml:p>Recently, when looking for a completely unrelated gnome-shell
extension, I stumbled across the <xhtml:a href="https://github.com/diegodario88/quake-terminal">Quake terminal
extension</xhtml:a>. I decided to give it a try to see if it was using a
built-in terminal library, or calling out to existing terminals,
and was surprised to discover it was the latter... and that it
found my Wezterm install!</xhtml:p>
<xhtml:p>Not only that, but it worked exactly as I would expect - it
dropped in the terminal from the top, at a partial height, and
hitting the hot key scrolled it back out.</xhtml:p>
<xhtml:h3>Customizing the terminal</xhtml:h3>
<xhtml:p>That said, I wanted to do a couple things different, and needed
to experiment:</xhtml:p>
<xhtml:ul>
<xhtml:li>I wanted the terminal to be partially transparent, so that I
can see what's under it on the screen.</xhtml:li>
<xhtml:li>I don't want window decorations for my dropdown terminal.</xhtml:li>
<xhtml:li>I want to use Wezterm's multiplexer, and open in a dedicated
workspace.</xhtml:li>
</xhtml:ul>
<xhtml:p>I'd achieved this in <xhtml:code>tdrop</xhtml:code> by writing a separate
command that <xhtml:code>tdrop</xhtml:code> would invoke, and I wondered if I
could do that here.</xhtml:p>
<xhtml:p>At first, I had no luck. The terminal would open, but as a
regular window, and not as a dropdown.</xhtml:p>
<xhtml:p>And then I did a bit more reading, and realized what I needed to
do.</xhtml:p>
<xhtml:p>I discovered that Quake Terminal looks for <xhtml:code>.desktop</xhtml:code>
files where the <xhtml:code>Categories</xhtml:code> field includes
<xhtml:code>TerminalEmulator</xhtml:code>. From there, the next important bit
is that the <xhtml:code>WM_CLASS</xhtml:code> of the running application has to
match the application ID.</xhtml:p>
<xhtml:p>In practice, this means the following:</xhtml:p>
<xhtml:ul>
<xhtml:li>The <xhtml:code>.desktop</xhtml:code> file needs the name to be a valid
application ID. The recommendation in the XDG spec is that this
follows the <xhtml:code>TLD.HOST.APP</xhtml:code> format, and needs to be
unique. I chose <xhtml:code>net.mwop.wezterm-dropdown</xhtml:code> for mine,
and thus the file is named
<xhtml:code>net.mwop.wezterm-dropdown.desktop</xhtml:code>.</xhtml:li>
<xhtml:li>When invoking Wezterm, I need to specify the
<xhtml:code>WM_CLASS</xhtml:code>. I can do this with the <xhtml:code>--class</xhtml:code>
option to the <xhtml:code>start</xhtml:code> subcommand: <xhtml:code>wezterm start
--class=net.mwop.wezterm-dropdown</xhtml:code>.</xhtml:li>
</xhtml:ul>
<xhtml:p>In the end, the full command I would run became:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">wezterm \
  --config <xhtml:span class="hljs-string">"window_background_opacity=0.85"</xhtml:span> \
  --config <xhtml:span class="hljs-string">"window_decorations='NONE'"</xhtml:span> \
  start \
    --cwd . \
    --class=net.mwop.dropdown-terminal \
    --domain unix \
    --attach \
    --workspace dropdown
</xhtml:code></xhtml:pre>
<xhtml:p>I specified this originally in the <xhtml:code>Exec=</xhtml:code> line of my
<xhtml:code>net.mwop.wezterm-dropdown.desktop</xhtml:code> file, but I found
that it would open a bare window on first execution, and only on
the second and subsequent executions would it become a
dropdown.</xhtml:p>
<xhtml:p>So I moved that invocation to the file
<xhtml:code>$HOME/.local/bin/wezterm-dropdown</xhtml:code>, and added an
<xhtml:code>exec</xhtml:code> at the front of it, and modified the desktop file
to read
<xhtml:code>Exec=/home/matthew/.local/bin/wezterm-dropdown</xhtml:code>. This
works slightly better - the first invocation opens it as a bare
window, but when you hit the hot key again, it hides it, and after
that, it opens as a dropdown. I can live with this.</xhtml:p>
<xhtml:h3>The final files</xhtml:h3>
<xhtml:p>The <xhtml:code>wezterm-dropdown</xhtml:code> script:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash"><xhtml:span class="hljs-meta">#!/usr/bin/zsh</xhtml:span>
<xhtml:span class="hljs-comment"># File: $HOME/.local/bin/wezterm-dropdown</xhtml:span>
<xhtml:span class="hljs-built_in">exec</xhtml:span> wezterm \
  --config <xhtml:span class="hljs-string">"window_background_opacity=0.85"</xhtml:span> \
  --config <xhtml:span class="hljs-string">"window_decorations='NONE'"</xhtml:span> \
  start \
    --cwd . \
    --class=net.mwop.dropdown-terminal \
    --domain unix \
    --attach \
    --workspace dropdown
</xhtml:code></xhtml:pre>
<xhtml:p>The <xhtml:code>net.mwop.wezterm-dropdown.desktop</xhtml:code> file (this
goes in <xhtml:code>$HOME/.local/share/applications/</xhtml:code>):</xhtml:p>
<xhtml:pre><xhtml:code class="language-ini hljs ini" data-lang="ini"><xhtml:span class="hljs-section">[Desktop Entry]</xhtml:span>
<xhtml:span class="hljs-attr">Name</xhtml:span>=Dropdown Terminal
<xhtml:span class="hljs-attr">Comment</xhtml:span>=Wezterm as a dropdown terminal
<xhtml:span class="hljs-attr">Keywords</xhtml:span>=shell<xhtml:span class="hljs-comment">;prompt;command;commandline;cmd;</xhtml:span>
<xhtml:span class="hljs-attr">Icon</xhtml:span>=org.wezfurlong.wezterm
<xhtml:span class="hljs-attr">StartupWMClass</xhtml:span>=org.wezfurlong.wezterm
<xhtml:span class="hljs-attr">Exec</xhtml:span>=/home/matthew/.local/bin/wezterm-dropdown
<xhtml:span class="hljs-attr">Type</xhtml:span>=Application
<xhtml:span class="hljs-attr">Categories</xhtml:span>=System<xhtml:span class="hljs-comment">;TerminalEmulator;Utility;</xhtml:span>
<xhtml:span class="hljs-attr">Terminal</xhtml:span>=<xhtml:span class="hljs-literal">false</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2024-09-17-wezterm-dropdown.html">Wezterm
Dropdown in Gnome</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2024-09-17T15:37:01-05:00">17 September
2024</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[How I use Wezterm]]></title>
    <published>2024-07-04T13:50:46-05:00</published>
    <updated>2024-09-17T15:41:46-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2024-07-04-how-i-use-wezterm.html"/>
    <id>https://mwop.net/blog/2024-07-04-how-i-use-wezterm.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I use the terminal a lot. Until the past few years, I basically
used <xhtml:em>only</xhtml:em> a browser and a terminal. (The primary changes
in the past couple years are that I'm using Logseq for tracking
notes and todos, and now use native apps for Zoom and Slack.)</xhtml:p>
<xhtml:p>Today I'm going to detail my exploration of <xhtml:a href="https://wezfurlong.org/wezterm/index.html">Wezterm</xhtml:a>, my current
daily driver.</xhtml:p>
<xhtml:h2>My terminal history</xhtml:h2>
<xhtml:p>I used gnome-terminal for a long time. At a certain point, I
learned about "Quake"-style, dropdown terminals, and realized that
these would be ideal for running one-off tasks or doing quick
lookups. I used <xhtml:a href="https://guake.github.io">Guake</xhtml:a> for a
number of years, as it was very similar to gnome-terminal.</xhtml:p>
<xhtml:p>At a certain point, however, I found gnome-terminal to be a bit
slow, and started looking for alternatives. I eventually landed on
one I'd used in my early days using LInux: <xhtml:a href="https://en.wikipedia.org/wiki/Xfce#Xfce_Terminal">xfce4-terminal</xhtml:a>.
This one was useful as it has a native dropdown mode, which meant I
could use the same terminal for dropdowns as well as my main
driver. It used very few resources, and "just worked."</xhtml:p>
<xhtml:p>Until it didn't. I adopted Wayland last year, as it was clear
that Ubuntu 2024.04 would likely default to it, if not outright
require it. And when I did, xfce4-terminal became an issue. I
cannot recall if it became slow (as it had to run via the Xorg
bindings), or if it outright didn't work, but I had to switch,
regardless.</xhtml:p>
<xhtml:p>I tried a number of terminals, settling on <xhtml:a href="https://sw.kovidgoyal.net/kitty/">Kitty</xhtml:a>. While it didn't have
a dropdown mode, I was able to accomplish it via <xhtml:a href="https://github.com/noctuid/tdrop">tdrop</xhtml:a>.</xhtml:p>
<xhtml:p>Now, the interesting thing is that I discovered tdrop when I was
investigating new terminal alternatives, and tried out <xhtml:a href="https://wezfurlong.org/wezterm/index.html">wezterm</xhtml:a>. At the
time, I didn't go with wezterm, as I found the configuration
confusing (I hadn't dove into Lua yet). I'll get back to wezterm
later.</xhtml:p>
<xhtml:h2>Tmux saves my bacon</xhtml:h2>
<xhtml:p>Now, one tool I use <xhtml:em>all the time</xhtml:em> in a terminal is
<xhtml:a href="https://github.com/tmux/tmux/wiki">tmux</xhtml:a>. Tmux
basically gives you windows and panes inside your terminal. On top
of that, you can have different sessions, and attach to and detach
from sessions independently; this allows you to attach to the same
session from multiple windows, or detach from a session you're not
actively working in so you can come back to it later. When you do
return, you have all the windows and panes that were open
previously.</xhtml:p>
<xhtml:p>Tmux is also pluggable, and I use a couple of plugins
regularly:</xhtml:p>
<xhtml:ul>
<xhtml:li>
<xhtml:p><xhtml:a href="https://github.com/christoomey/vim-tmux-navigator">vim-tmux-navigator</xhtml:a>
is technically a vim/nvim plugin, but it works in tandem with
configuration you set it tmux itself. What it does is make it so
that you can use the same key combinations to navigate between
vim/nvim and tmux panes. So, let's say you have vim in a tmux pane
on the left, and it has two vertical panes. If you're in the
right-most vim pane, and hit the keystroke to move right, with this
plugin, that moves you into the tmux pane to the right.</xhtml:p>
<xhtml:p>This is tremendously useful, as you don't need to think about
what context you're in as you move around between vim/nvim and tmux
panes.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p><xhtml:a href="https://github.com/tmux-plugins/tmux-resurrect">tmux-resurrect</xhtml:a>
allows you to persist sessions between tmux server invocations. In
other words, if you restart your computer, the first time you start
tmux, all your sessions re-appear, and each has any previous
history already present. This plugin is has helped me feel
confident that I won't lose track of what I was doing if I need to
reboot.</xhtml:p>
</xhtml:li>
</xhtml:ul>
<xhtml:p>What I have done for many years is setup my dropdown terminal to
create or attach to a named tmux session. This means that I always
have the ability to create new windows and panes as needed from my
dropdown terminal, which is hugely useful.</xhtml:p>
<xhtml:p>Additionally, most times I open a terminal, I'm starting a tmux
session named to reflect what I'm doing. This ensures if I need to
reboot, I don't lose my place. In fact, I find myself getting upset
quite often when I start working on something and only later
realize I didn't start tmux, as I have to quit what I'm doing and
start back up after starting tmux. And when I SSH to another
server, I hate not having tmux available; I'll often use tmux
locally, which, while it means I need multiple SSH sessions, it at
least gives me what I need for functionality.</xhtml:p>
<xhtml:p>I depend on terminal multiplexing. It's a basic feature I
absolutely need.</xhtml:p>
<xhtml:h2>Learning wezterm</xhtml:h2>
<xhtml:p>When I first came across wezterm, one thing I noted is that it
had a built-in multiplexing. However, it was sufficiently different
from tmux that I only noted it in passing.</xhtml:p>
<xhtml:p>Sometime this past winter, I became reacquainted with Wez
Furlong, author of wezterm, via Mastodon, where he's fairly active.
Wez used to contribute heavily to PHP, and we would cross paths at
PHP conferences in the late oughts, and it was fun to see what he's
been up to since — which includes creating wezterm. As such, I
figured I'd try it out again.</xhtml:p>
<xhtml:p>It soon became my daily driver. While it took a bit to get it
working well with tdrop, once I did, I found it was fast,
unobtrusive, and "just worked".</xhtml:p>
<xhtml:p>But at the back of my head, I kept thinking, "I wonder if this
could replace tmux for me?"</xhtml:p>
<xhtml:p>When I looked at what I actually <xhtml:em>use</xhtml:em> in tmux, I found
that it was just these things:</xhtml:p>
<xhtml:p>_ Powerline. And, honestly, I discovered that I never actually
LOOK at my powerline, other than to see what session and window I'm
in. I decided that I'd be happy with minimally seeing active
windows, better yet the session, and only vaguely interested in
seeing anything else (CPU usage, memory usage, time, etc.).</xhtml:p>
<xhtml:ul>
<xhtml:li>Scrollback per pane</xhtml:li>
<xhtml:li>Select and copy</xhtml:li>
<xhtml:li>"Zoom"ing a pane (taking a single pane as "fullscreen" within a
window, and then restoring to the previous layout)</xhtml:li>
<xhtml:li>vim-tmux-navigator (see the description in the previous
section)</xhtml:li>
<xhtml:li>tmux-resurrect (natch)</xhtml:li>
</xhtml:ul>
<xhtml:p>This is not a huge featureset, so I set out to see if I could do
these things.</xhtml:p>
<xhtml:blockquote>
<xhtml:p>One note: I never did figure out how to show the current session
name in the tab bar. Otherwise, I accomplished all other goals!</xhtml:p>
</xhtml:blockquote>
<xhtml:h3>Lua</xhtml:h3>
<xhtml:p>Wezterm, while written in Rust, manages configuration using
<xhtml:a href="https://www.lua.org">Lua</xhtml:a>. As it turns out... Lua can
be picked up very quickly if you've used basically any programming
language at all. It looks a lot like JSON, but with a limited
number of statements and expressions also available.</xhtml:p>
<xhtml:p>Wezterm ships with a small number of modules that allow you to
configure the terminal, as well as perform a certain number of
actions, from interacting with sessions, windows, and panes, to
performing prompts.</xhtml:p>
<xhtml:p>Once I picked up Lua, configuring Wezterm became relatively
straightforward. I'm now considering updating my nvim configuration
to be entirely in Lua as well!</xhtml:p>
<xhtml:h3>Take me to your Leader</xhtml:h3>
<xhtml:p>Tmux and vim/nvim each have a concept of a <xhtml:em>leader</xhtml:em>
character or key sequence. These allow you a bit more flexibility
when creating keyboard shortcuts, as you don't need to worry about
conflicts with other programs. The program "screen" (the OG
terminal multiplexer) uses "Ctrl-A" as the leader, while tmux uses
"Ctrl-B" by default (but most tmux users remap it to Ctrl-A). In
vim, I map <xhtml:code>,</xhtml:code> as my leader character.</xhtml:p>
<xhtml:p>Wezterm allows defining a leader as well. I decided I'd use
"Ctrl-A", as the plan is to replace tmux:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.leader = {
  key = <xhtml:span class="hljs-string">'a'</xhtml:span>,
  mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
  timeout_milliseconds = <xhtml:span class="hljs-number">2000</xhtml:span>,
}
</xhtml:code></xhtml:pre>
<xhtml:h3>Scrollback, select, and copy</xhtml:h3>
<xhtml:p>Wezterm allows scrollback in its copy mode. I setup a keybinding
mimicing the backscroll/copy mode in tmux:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">'['</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = wezterm.action.ActivateCopyMode,
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>Interestingly, I found the copy mode in wezterm to be more
predictable than in tmux!</xhtml:p>
<xhtml:h3>Zooming</xhtml:h3>
<xhtml:p>Sometimes I'm in a pane within a split window, and realize I
need a bit more visual room. I had mapped <xhtml:code>Alt-F</xhtml:code> to
this in tmux, so I did the same with wezterm.</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">'f'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
    action = wezterm.action.TogglePaneZoomState,
  },
}
</xhtml:code></xhtml:pre>
<xhtml:h3>Windows versus Tabs</xhtml:h3>
<xhtml:p>Tmux calls them windows, but wezterm calls them tabs. The idea
is the same: a discrete, indexed and/or named terminal that can
handle one or more panes.</xhtml:p>
<xhtml:p>Tmux puts the list of windows at the bottom by default (I
<xhtml:em>think</xhtml:em> this can be configured, but I've literally only ever
had them at the bottom of the screen). Wezterm puts them at the
top. I wanted to switch the behavior, but could not find the
setting... so I popped into the <xhtml:a href="https://app.element.io/#/room/#wezterm:matrix.org">wezterm matrix
room</xhtml:a> to ask. Wez responded within minutes, and pointed me to
the <xhtml:a href="https://wezfurlong.org/wezterm/config/lua/config/tab_bar_at_bottom.html">
<xhtml:code>tab_bar_at_bottom</xhtml:code> setting</xhtml:a>. (The matrix room is
incredibly friendly and positive!)</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.tab_bar_at_bottom = <xhtml:span class="hljs-literal">true</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>How do I create a tab? Wezterm, like most terminals, uses
<xhtml:code>Ctrl-Shift-Tab</xhtml:code>, but with tmux, I would use
<xhtml:code>&lt;Leader&gt;c</xhtml:code>, so I added that binding:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">'c'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.SpawnTab <xhtml:span class="hljs-string">'CurrentPaneDomain'</xhtml:span>,
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>Next, I wanted a way to move between tabs without a mouse. I
mapped these the same as I did in tmux:
<xhtml:code>&lt;Leader&gt;n</xhtml:code> for the next tab,
<xhtml:code>&lt;Leader&gt;p</xhtml:code> for the previous:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">'n'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = wezterm.action.ActivateTabRelative(<xhtml:span class="hljs-number">1</xhtml:span>),
  },
  {
    key = <xhtml:span class="hljs-string">'p'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = wezterm.action.ActivateTabRelative(<xhtml:span class="hljs-number">-1</xhtml:span>),
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>I like to name my windows/tabs. This helps me understand what
work I'm doing in each: tests, running docker images, etc. In tmux,
I use <xhtml:code>&lt;Leader&gt;,</xhtml:code> to do this, and this was where
my first real Lua came in, as I had to define a callback with a
conditional to make it happen:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">','</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.PromptInputLine {
      description = <xhtml:span class="hljs-string">'Enter new name for tab'</xhtml:span>,
      action = wezterm.action_callback(
        <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, line)</xhtml:span></xhtml:span>
          <xhtml:span class="hljs-keyword">if</xhtml:span> line <xhtml:span class="hljs-keyword">then</xhtml:span>
            window:active_tab():set_title(line)
          <xhtml:span class="hljs-keyword">end</xhtml:span>
        <xhtml:span class="hljs-keyword">end</xhtml:span>
      ),
    },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>How about switching to a specific tab? With tmux, you would do
<xhtml:code>&lt;Leader&gt;&lt;index&gt;</xhtml:code>, where
<xhtml:code>&lt;index&gt;</xhtml:code> is the index of the window you want
(e.g. "1" or "3"). For wezterm, I decided to use the tab navigator,
which gives a dialog displaying each tab with their index; you then
specify the one you want, either by navigating to it, or entering
the index. Even better, you can use <xhtml:code>/</xhtml:code> to filter
through them, which is handy if you have more than ten windows, as
otherwise indexes only get you from 0-9!</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">'w'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.ShowTabNavigator,
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>I also want to be able to kill/close a tab without needing to
type "exit"; this is particularly important if a process is stuck.
Again, I went with what I knew from tmux, and used
<xhtml:code>&lt;Leader&gt;&amp;</xhtml:code>... and learned a valuable lesson
in wezterm configuration in the process!</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Close tab</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'&amp;'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act.CloseCurrentTab{ confirm = <xhtml:span class="hljs-literal">true</xhtml:span> },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:blockquote>
<xhtml:p>As it turns out, if a character requires hitting the shift key
normally, such as any of the symbols above the numeric keys, or
even keys like <xhtml:code>?</xhtml:code>, <xhtml:code>&lt;</xhtml:code>,
<xhtml:code>&gt;</xhtml:code>, the <xhtml:code>|</xhtml:code> and
<xhtml:code>{</xhtml:code>/<xhtml:code>}</xhtml:code> symbols, the <xhtml:code>SHIFT</xhtml:code> mod
MUST be included!</xhtml:p>
</xhtml:blockquote>
<xhtml:p>Finally, there were a smattering of other configuration settings
I changed to make the behavior match my expectations, as well as to
make it visually easier to parse:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-comment">-- Make it look like tabs, with better GUI controls</xhtml:span>
wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.use_fancy_tab_bar = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-comment">-- Don't let any individual tab name take too much room</xhtml:span>
wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.tab_max_width = <xhtml:span class="hljs-number">32</xhtml:span>
wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.colors = {
  tab_bar = {
    active_tab = {
      <xhtml:span class="hljs-comment">-- I use a solarized dark theme; this gives a teal background to the active tab</xhtml:span>
      fg_color = <xhtml:span class="hljs-string">'#073642'</xhtml:span>
      bg_color = <xhtml:span class="hljs-string">'#2aa198'</xhtml:span>
    }
  }
}
<xhtml:span class="hljs-comment">-- Switch to the last active tab when I close a tab</xhtml:span>
wezterm.<xhtml:span class="hljs-built_in">config</xhtml:span>.switch_to_last_active_tab_when_closing_tab = <xhtml:span class="hljs-literal">true</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>I really appreciate the attention to detail when naming
configuration options. Once you figure out what option you need,
you don't need to look up what it does!</xhtml:p>
<xhtml:h3>Panes</xhtml:h3>
<xhtml:p>The huge benefit that screen, and later, tmux, introduced is the
ability to have multiple <xhtml:em>panes</xhtml:em> within a window. This
allows you to essentially treat your terminal like you would a
tiled window manager. During my years as an engineer, this allowed
me to treat my CLI as an IDE: I'd have one pane open with vim/nvim,
split between a test case and the source code I was modifying;
another would be used to run unit tests, which also gave me the
ability to scroll back and view specific error messages and
traces.</xhtml:p>
<xhtml:p>So if I was going to use wezterm, I'd need to be able to split
my window/tab into panes. Additionally, I would need the ability to
keep a fair amount of scrollback history, be able to use my mouse
when desired, etc. On top of all that, I'd want to be able to
navigate seamlessly between vim/nvim panes and wezterm panes.</xhtml:p>
<xhtml:p>Let's tackle the (n)vim &lt;-&gt; wezterm pane issues first, as
this also dictates keybindings eventually. I found <xhtml:a href="https://github.com/aca/wezterm.nvim">wezterm.nvim</xhtml:a>, which,
despite its name, interacts primarily with wezterm.</xhtml:p>
<xhtml:p>The first step of setup is to modify your shell configuration to
add the following line to your shell configuation (assuming you use
bash or zsh):</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">[ -n <xhtml:span class="hljs-string">"<xhtml:span class="hljs-variable">$WEZTERM_PANE</xhtml:span>"</xhtml:span> ] &amp;&amp; <xhtml:span class="hljs-built_in">export</xhtml:span> NVIM_LISTEN_ADDRESS=<xhtml:span class="hljs-string">"/tmp/nvim<xhtml:span class="hljs-variable">$WEZTERM_PANE</xhtml:span>"</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Once that's in place, you clone the project, and use the
<xhtml:code>go</xhtml:code> language compiler to build and install it; this
will install the utility<xhtml:code>wezterm.nvim.navigator</xhtml:code>. Run
<xhtml:code>which wezterm.nvim.navigator</xhtml:code> when done to find the
path on your sytem where it's installed, as it will be needed to
setup your wezterm configuration.</xhtml:p>
<xhtml:p>From there, you define a function in the configuration that uses
that utility to determine if you're in a vim/nvim pane or in a
wezterm pane, and, from there, determines what needs to be done to
move in the direction requested. You then also define a number of
event handlers that invoke this function, and finally some
keybindings to trigger those events.</xhtml:p>
<xhtml:p>The beauty of this is that you don't need <xhtml:em>anything</xhtml:em> in
your vim/nvim configuration; it just uses the default keybindings
to move between panes, as wezterm now invokes them!</xhtml:p>
<xhtml:p>Now, on top of this, you can <xhtml:em>also</xhtml:em> specify some
keybindings for <xhtml:em>resizing</xhtml:em> panes, which is really useful.
This requires setting up some very similar functionality (a
function to perform the resizing, event handlers to invoke it, and
keybindings).</xhtml:p>
<xhtml:p>I bound <xhtml:code>&lt;Ctrl&gt;-(hjkl)</xhtml:code> for moving between
windows (these are the standard vim movement mappings), and
<xhtml:code>&lt;Alt&gt;-(hjkl)</xhtml:code> for resizing.</xhtml:p>
<xhtml:p>The full configuration for these motions is here:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-comment">-- Pull in the wezterm API</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-built_in">os</xhtml:span>              = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'os'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> wezterm         = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'wezterm'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> act             = wezterm.action

<xhtml:span class="hljs-comment">-- This table will hold the configuration.</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-built_in">config</xhtml:span> = {}

<xhtml:span class="hljs-comment">-- In newer versions of wezterm, use the config_builder which will</xhtml:span>
<xhtml:span class="hljs-comment">-- help provide clearer error messages</xhtml:span>
<xhtml:span class="hljs-keyword">if</xhtml:span> wezterm.config_builder <xhtml:span class="hljs-keyword">then</xhtml:span>
  <xhtml:span class="hljs-built_in">config</xhtml:span> = wezterm.config_builder()
<xhtml:span class="hljs-keyword">end</xhtml:span>


<xhtml:span class="hljs-keyword">local</xhtml:span> move_around = <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, direction_wez, direction_nvim)</xhtml:span></xhtml:span>
  <xhtml:span class="hljs-keyword">local</xhtml:span> result = <xhtml:span class="hljs-built_in">os</xhtml:span>.<xhtml:span class="hljs-built_in">execute</xhtml:span>(<xhtml:span class="hljs-string">"env NVIM_LISTEN_ADDRESS=/tmp/nvim"</xhtml:span> .. pane:pane_id() .. <xhtml:span class="hljs-string">" "</xhtml:span> .. wezterm.home_dir .. <xhtml:span class="hljs-string">"/.local/bin/wezterm.nvim.navigator"</xhtml:span> .. <xhtml:span class="hljs-string">" "</xhtml:span> .. direction_nvim)
  <xhtml:span class="hljs-keyword">if</xhtml:span> result <xhtml:span class="hljs-keyword">then</xhtml:span>
  window:perform_action(
      act({ SendString = <xhtml:span class="hljs-string">"\x17"</xhtml:span> .. direction_nvim }),
      pane
    )
  <xhtml:span class="hljs-keyword">else</xhtml:span>
    window:perform_action(
      act({ ActivatePaneDirection = direction_wez }),
      pane
    )
  <xhtml:span class="hljs-keyword">end</xhtml:span>
<xhtml:span class="hljs-keyword">end</xhtml:span>

wezterm.on(<xhtml:span class="hljs-string">"move-left"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Left"</xhtml:span>, <xhtml:span class="hljs-string">"h"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-right"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Right"</xhtml:span>, <xhtml:span class="hljs-string">"l"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-up"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Up"</xhtml:span>, <xhtml:span class="hljs-string">"k"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-down"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Down"</xhtml:span>, <xhtml:span class="hljs-string">"j"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

<xhtml:span class="hljs-keyword">local</xhtml:span> vim_resize = <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, direction_wez, direction_nvim)</xhtml:span></xhtml:span>
        <xhtml:span class="hljs-keyword">local</xhtml:span> result = <xhtml:span class="hljs-built_in">os</xhtml:span>.<xhtml:span class="hljs-built_in">execute</xhtml:span>(
                <xhtml:span class="hljs-string">"env NVIM_LISTEN_ADDRESS=/tmp/nvim"</xhtml:span>
                        .. pane:pane_id()
                        .. <xhtml:span class="hljs-string">" "</xhtml:span>
            .. wezterm.home_dir
                        .. <xhtml:span class="hljs-string">"/.local/bin/wezterm.nvim.navigator"</xhtml:span>
                        .. <xhtml:span class="hljs-string">" "</xhtml:span>
                        .. direction_nvim
        )
        <xhtml:span class="hljs-keyword">if</xhtml:span> result <xhtml:span class="hljs-keyword">then</xhtml:span>
                window:perform_action(act({ SendString = <xhtml:span class="hljs-string">"\x1b"</xhtml:span> .. direction_nvim }), pane)
        <xhtml:span class="hljs-keyword">else</xhtml:span>
                window:perform_action(act({ ActivatePaneDirection = direction_wez }), pane)
        <xhtml:span class="hljs-keyword">end</xhtml:span>
<xhtml:span class="hljs-keyword">end</xhtml:span>

wezterm.on(<xhtml:span class="hljs-string">"resize-left"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Left"</xhtml:span>, <xhtml:span class="hljs-string">"h"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-right"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Right"</xhtml:span>, <xhtml:span class="hljs-string">"l"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-up"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Up"</xhtml:span>, <xhtml:span class="hljs-string">"k"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-down"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Down"</xhtml:span>, <xhtml:span class="hljs-string">"j"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
    <xhtml:span class="hljs-comment">-- CTRL + (h,j,k,l) to move between panes</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'h'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-left"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'j'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-down"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'k'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-up"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'l'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-right"</xhtml:span> }),
    },
    <xhtml:span class="hljs-comment">-- ALT + (h,j,k,l) to resize panes</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'h'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-left"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'j'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-down"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'k'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-up"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'l'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-right"</xhtml:span> }),
    },
}
</xhtml:code></xhtml:pre>
<xhtml:p>So this gives me movement, but how do I actually <xhtml:em>split</xhtml:em>
the window into panes? For this, I've always favored
<xhtml:code>&lt;Leader&gt;|</xhtml:code> to split into vertical panes, and
<xhtml:code>&lt;Leader&gt;-</xhtml:code> to split into horizontal panes:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Vertical split</xhtml:span>
  {
    <xhtml:span class="hljs-comment">-- |</xhtml:span>
    key = <xhtml:span class="hljs-string">'|'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act.SplitPane {
      direction = <xhtml:span class="hljs-string">'Right'</xhtml:span>,
      size = { Percent = <xhtml:span class="hljs-number">50</xhtml:span> },
    },
  },
  <xhtml:span class="hljs-comment">-- Horizontal split</xhtml:span>
  {
    <xhtml:span class="hljs-comment">-- -</xhtml:span>
    key = <xhtml:span class="hljs-string">'-'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.SplitPane {
      direction = <xhtml:span class="hljs-string">'Down'</xhtml:span>,
      size = { Percent = <xhtml:span class="hljs-number">50</xhtml:span> },
    },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>Sometimes I want to swap a pane with another one, because
they're not positioned the way I'd like. I bound
<xhtml:code>&lt;Leader&gt;{</xhtml:code> to the wezterm <xhtml:code>PaneSelect</xhtml:code>
action, which allows me to choose the pane I wish to swap with
using an alphanumerical index:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    <xhtml:span class="hljs-comment">-- |</xhtml:span>
    key = <xhtml:span class="hljs-string">'{'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act.PaneSelect { mode = <xhtml:span class="hljs-string">'SwapWithActiveKeepFocus'</xhtml:span> }
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>And sometimes I like to move back and forth between panes; I use
<xhtml:code>&lt;Leader&gt;;</xhtml:code> and <xhtml:code>&lt;Leader&gt;;</xhtml:code> for
that:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  {
    key = <xhtml:span class="hljs-string">';'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.ActivatePaneDirection(<xhtml:span class="hljs-string">'Prev'</xhtml:span>),
  },
  {
    key = <xhtml:span class="hljs-string">'o'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.ActivatePaneDirection(<xhtml:span class="hljs-string">'Next'</xhtml:span>),
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>With all of these in place, I'm very nearly there. Just a couple
of minor settings:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.pane_focus_follows_mouse = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.scrollback_lines = <xhtml:span class="hljs-number">5000</xhtml:span>
<xhtml:span class="hljs-comment">-- I don't really have need for padding between panes</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.window_padding = {
  left = <xhtml:span class="hljs-number">0</xhtml:span>,
  right = <xhtml:span class="hljs-number">0</xhtml:span>,
  top = <xhtml:span class="hljs-number">0</xhtml:span>,
  bottom = <xhtml:span class="hljs-number">0</xhtml:span>,
}
</xhtml:code></xhtml:pre>
<xhtml:h3>Sessions</xhtml:h3>
<xhtml:p>Next up on my list of functionality: sessions.</xhtml:p>
<xhtml:p>As noted before, tmux sessions, paired with tmux-resurrect, have
given me confidence that I won't lose work if I need to restart my
computer, or if the battery dies. How can I reproduce this with
wezterm?</xhtml:p>
<xhtml:p>By default, each wezterm instance defines its own
<xhtml:em>workspaces</xhtml:em>, which are analogous to tmux sessions... kind
of. You can create additional workspaces and switch between
workspaces using a variety of tools.</xhtml:p>
<xhtml:p>However, if you want to share workspaces <xhtml:em>between</xhtml:em>
wezterm instances, you need to have a <xhtml:em>mux domain</xhtml:em> running,
and <xhtml:em>connect</xhtml:em> to it. A <xhtml:em>mux domain</xhtml:em> is a set of
workspaces, windows, and tabs. By default, a "local" domain is
created everytime you start a wezterm terminal, but you can define
additional domains as well, including remote mux servers running on
an SSH-accessible server or TLS-guarded remote port, or a simple
unix domain.</xhtml:p>
<xhtml:p>In my case, I'm configuring a unix domain to run out of the
box:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.unix_domains = {
  {
    name = <xhtml:span class="hljs-string">'unix'</xhtml:span>,
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>This tells wezterm to setup a mux server on a unix socket. It
does not connect to it out of the box, but it does ensure it's
running.</xhtml:p>
<xhtml:p>From there, I've created two keybindings: one for connecting to
the mux server, and another for detaching from it. This approach
somewhat mirrors how you use tmux: you have to start tmux to create
sessions, and detaching from it will drop you back into your
original shell.</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Attach to muxer</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'a'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.AttachDomain <xhtml:span class="hljs-string">'unix'</xhtml:span>,
  },

  <xhtml:span class="hljs-comment">-- Detach from muxer</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'d'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.DetachDomain { DomainName = <xhtml:span class="hljs-string">'unix'</xhtml:span> },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>With the muxer, I can create <xhtml:em>workspaces</xhtml:em>. These are
analogous to tmux sessions, and are a group of windows and panes.
When you start a wezterm terminal, you're in a "default" workspace.
So the first thing to do is to allow <xhtml:em>renaming</xhtml:em> the
workspace to reflect what I'm working on:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Rename current session; analagous to command in tmux</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'$'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act.PromptInputLine {
      description = <xhtml:span class="hljs-string">'Enter new name for session'</xhtml:span>,
      action = wezterm.action_callback(
        <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, line)</xhtml:span></xhtml:span>
          <xhtml:span class="hljs-keyword">if</xhtml:span> line <xhtml:span class="hljs-keyword">then</xhtml:span>
            mux.rename_workspace(
              window:mux_window():get_workspace(),
              line
            )
          <xhtml:span class="hljs-keyword">end</xhtml:span>
        <xhtml:span class="hljs-keyword">end</xhtml:span>
      ),
    },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>The above maps <xhtml:code>&lt;Leader&gt;$</xhtml:code> to prompt you to
rename the session.</xhtml:p>
<xhtml:p>From here, how do I switch to another workspace, or create a new
one? For that, I mapped <xhtml:code>&lt;Leader&gt;s</xhtml:code> to show the
workspace launcher; this <xhtml:em>lists</xhtml:em> available workspaces,
allows you to <xhtml:em>switch</xhtml:em> between them, as well as
<xhtml:em>create</xhtml:em> new workspaces:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Show list of workspaces</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'s'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
    action = act.ShowLauncherArgs { flags = <xhtml:span class="hljs-string">'WORKSPACES'</xhtml:span> },
  },
}
</xhtml:code></xhtml:pre>
<xhtml:p>The workflow then looks like this:</xhtml:p>
<xhtml:ul>
<xhtml:li>
<xhtml:p>Start wezterm.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>If I want to be able to switch between mux workspaces on other
windows, I then hit <xhtml:code>&lt;Leader&gt;a</xhtml:code>. This will connect
to the mux server, and throw me into the <xhtml:code>default</xhtml:code>
workspace.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>If I want to leave the mux session, I can hit
<xhtml:code>&lt;Leader&gt;d</xhtml:code>, which will detach me from it, taking
me back to the "Local" domain for the GUI window I'm in.</xhtml:p>
<xhtml:ul>
<xhtml:li>At this point, typing <xhtml:code>exit</xhtml:code> will close the window,
as I'll have left the last pane of the last window in the
workspace.</xhtml:li>
</xhtml:ul>
</xhtml:li>
</xhtml:ul>
<xhtml:p>One thing that is odd about the situation: if you are in a mux
session with multiple workspaces, and you <xhtml:code>exit</xhtml:code> out of
the last pane of the last window of a workspace... it doesn't close
the window or drop you out of the mux domain. Instead, it takes you
to the last active workspace. This can be confusing at first.
However, all you need to do at that point is hit
<xhtml:code>&lt;Leader&gt;d</xhtml:code> to detach, and that will take you back
to the local domain, from which you can exit normally.</xhtml:p>
<xhtml:p>Interestingly, you can use the system controls to close the
window at any point (e.g., hitting the close button, or using a
keybinding like <xhtml:code>&lt;Ctrl&gt;w</xhtml:code> or
<xhtml:code>&lt;Alt&gt;F4</xhtml:code>). The mux server will keep the state for
you. Of course, if you do this and you don't want the workspace to
persist, you'll need to return to that workspace at some point and
exit out of it.</xhtml:p>
<xhtml:h3>Keeping things safe</xhtml:h3>
<xhtml:p>Now that I have a concept of sessions, what about recovery? One
of my most used features in tmux was the tmux-resurrect plugin,
which allowed me to save and restore sessions (and did some
background periodic saving for me). Can I do that with wezterm?</xhtml:p>
<xhtml:p>Yes, to an extent, via the <xhtml:a href="https://github.com/danielcopper/wezterm-session-manager">wezterm-session-manager
project</xhtml:a>. This is a Lua module that you add to your wezterm
configuration; you clone it in the
<xhtml:code>$HOME/.config/wezterm/</xhtml:code> directory, and then import it
into your configuration:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-keyword">local</xhtml:span> session_manager = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'wezterm-session-manager/session-manager'</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>From there, you need to register some event listeners:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua">wezterm.on(<xhtml:span class="hljs-string">"save_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.save_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)
wezterm.on(<xhtml:span class="hljs-string">"load_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.load_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)
wezterm.on(<xhtml:span class="hljs-string">"restore_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.restore_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)
</xhtml:code></xhtml:pre>
<xhtml:p>Finally, add some keybindings; I registered
<xhtml:code>&lt;Leader&gt;S</xhtml:code> to save the session,
<xhtml:code>&lt;Leader&gt;l</xhtml:code> to load a session (this doesn't
currently work), and <xhtml:code>&lt;Leader&gt;R</xhtml:code> to restore from
the most recent previous save point:</xhtml:p>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
  <xhtml:span class="hljs-comment">-- Session manager bindings</xhtml:span>
  {
    key = <xhtml:span class="hljs-string">'s'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act({ EmitEvent = <xhtml:span class="hljs-string">"save_session"</xhtml:span> }),
  },
  {
    key = <xhtml:span class="hljs-string">'L'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act({ EmitEvent = <xhtml:span class="hljs-string">"load_session"</xhtml:span> }),
  },
  {
    key = <xhtml:span class="hljs-string">'R'</xhtml:span>,
    mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
    action = act({ EmitEvent = <xhtml:span class="hljs-string">"restore_session"</xhtml:span> }),
  },
}
</xhtml:code></xhtml:pre>
<xhtml:h3>Ready to Launch</xhtml:h3>
<xhtml:p>Wezterm ships with an XDG desktop file, which works, but it will
always open the same window by default. Also, I prefer to have a
simpler shortcut than <xhtml:code>&lt;Super&gt;</xhtml:code> + <xhtml:em>type
something to search for the app</xhtml:em>. As such, I usually bind
<xhtml:code>&lt;Super&gt;t</xhtml:code> to open my terminal.</xhtml:p>
<xhtml:p>For the actual command, I use <xhtml:code>wezterm-gui start
--always-new-process</xhtml:code>. This ensures that I get a new window
each time, and that it's not bound to another domain or workspace
at start; I can use my other keybindings to attach to a domain,
rename or create new workspaces, etc.</xhtml:p>
<xhtml:h3>Watch for the drop!</xhtml:h3>
<xhtml:blockquote>
<xhtml:h4>Update 2024-09-17</xhtml:h4>
<xhtml:p>I have found a new solution for implementing a dropdown terminal
via the gnome-shell Quake Terminal extension; you can <xhtml:a href="/blog/2024-09-17-wezterm-dropdown.html">read my blog post about
setting up Quake Terminal with Wezterm</xhtml:a> for instructions.</xhtml:p>
</xhtml:blockquote>
<xhtml:details>
<xhtml:summary>Click to expand my old setup using tdrop</xhtml:summary>
<xhtml:p>I mentioned earlier that I find a dropdown terminal handy, and
that I started using <xhtml:a href="https://github.com/noctuid/tdrop">tdrop</xhtml:a> to provide one when I
adopted <xhtml:code>kitty</xhtml:code> as my terminal. I was able to do the
same with wezterm.</xhtml:p>
<xhtml:p>A few things to note:</xhtml:p>
<xhtml:ul>
<xhtml:li>
<xhtml:p>tdrop does NOT play well with Wayland, but it provides a sneaky
way to work within Wayland. You can prefix your command with
<xhtml:code>WAYLAND_DISPLAY=no</xhtml:code> to force usage of XWayland when
invoking your terminal. I had to do this with Wezterm, as otherwise
it simply didn't work. It would spawn the terminal, but without
resizing it.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>For my dropdown terminal, I like to have it slightly
transparent, and without window decorations.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>I ALWAYS want this one using the mux domain. Since it's always
running, I need to be able to look back at history and keep the
panes in the same state between reboots or logging out.</xhtml:p>
</xhtml:li>
</xhtml:ul>
<xhtml:p>To make things simpler for myself, I created a simple shell
script:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash"><xhtml:span class="hljs-meta">#!/bin/bash</xhtml:span>
<xhtml:span class="hljs-comment"># File: $HOME/.local/bin/dropdown-terminal </xhtml:span>
WAYLAND_DISPLAY=no \
  <xhtml:span class="hljs-variable">$HOME</xhtml:span>/.<xhtml:span class="hljs-built_in">local</xhtml:span>/bin/tdrop --class=dropdown -mta \
  wezterm-gui \
    --config <xhtml:span class="hljs-string">"window_background_opacity=0.85"</xhtml:span> \
    --config <xhtml:span class="hljs-string">"window_decorations='NONE'"</xhtml:span> \
    start \
      --domain unix \
      --attach \
      --workspace dropdown
</xhtml:code></xhtml:pre>
<xhtml:p>What does this do?</xhtml:p>
<xhtml:ul>
<xhtml:li>
<xhtml:p>I have <xhtml:code>tdrop</xhtml:code> in my <xhtml:code>$HOME/.local/bin/</xhtml:code>
directory. I have found gnome-shell will not look in that path,
despite it being in my shell profile, so I reference it fully.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>I set the window class for the generated window.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>And I tell <xhtml:code>tdrop</xhtml:code> to be aware of which monitor I'm
on, and autosize the terminal based on the monitor dimensions.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>I tell wezterm to override two configuration values:</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>I set the background opacity to 0.85, making it slightly
transparent</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>I disable window decorations</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p>and I tell it to connect to the "unix" domain on startup, attach
to the existing window, and use the "dropdown" workspace (creating
it if it does not exist).</xhtml:p>
</xhtml:li>
</xhtml:ul>
<xhtml:p>In my gnome-shell keybindings, I bind <xhtml:code>&lt;F12&gt;</xhtml:code>
to run <xhtml:code>$HOME/.local/bin/dropdown-terminal</xhtml:code>.</xhtml:p>
<xhtml:p><xhtml:code>tdrop</xhtml:code> will then toggle the dropdown state everytime
I hit that key. By default, it takes up 50% of the screen.</xhtml:p>
</xhtml:details>
<xhtml:h2>The full configuration</xhtml:h2>
<xhtml:p>Here it is. I'm sure there's stuff I could do better, but it
works.</xhtml:p>
<xhtml:details>
<xhtml:summary>wezterm.lua (click to expand/hide)</xhtml:summary>
<xhtml:pre><xhtml:code class="language-lua hljs lua" data-lang="lua"><xhtml:span class="hljs-comment">-- Pull in the wezterm API</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-built_in">os</xhtml:span>              = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'os'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> wezterm         = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'wezterm'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> session_manager = <xhtml:span class="hljs-built_in">require</xhtml:span> <xhtml:span class="hljs-string">'wezterm-session-manager/session-manager'</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> act             = wezterm.action
<xhtml:span class="hljs-keyword">local</xhtml:span> mux             = wezterm.mux

<xhtml:span class="hljs-comment">-- --------------------------------------------------------------------</xhtml:span>
<xhtml:span class="hljs-comment">-- FUNCTIONS AND EVENT BINDINGS</xhtml:span>
<xhtml:span class="hljs-comment">-- --------------------------------------------------------------------</xhtml:span>

<xhtml:span class="hljs-comment">-- Session Manager event bindings</xhtml:span>
<xhtml:span class="hljs-comment">-- See https://github.com/danielcopper/wezterm-session-manager</xhtml:span>
wezterm.on(<xhtml:span class="hljs-string">"save_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.save_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)
wezterm.on(<xhtml:span class="hljs-string">"load_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.load_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)
wezterm.on(<xhtml:span class="hljs-string">"restore_session"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window)</xhtml:span></xhtml:span> session_manager.restore_state(window) <xhtml:span class="hljs-keyword">end</xhtml:span>)

<xhtml:span class="hljs-comment">-- Wezterm &lt;-&gt; nvim pane navigation</xhtml:span>
<xhtml:span class="hljs-comment">-- You will need to install https://github.com/aca/wezterm.nvim</xhtml:span>
<xhtml:span class="hljs-comment">-- and ensure you export NVIM_LISTEN_ADDRESS per the README in that repo</xhtml:span>

<xhtml:span class="hljs-keyword">local</xhtml:span> move_around = <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, direction_wez, direction_nvim)</xhtml:span></xhtml:span>
    <xhtml:span class="hljs-keyword">local</xhtml:span> result = <xhtml:span class="hljs-built_in">os</xhtml:span>.<xhtml:span class="hljs-built_in">execute</xhtml:span>(<xhtml:span class="hljs-string">"env NVIM_LISTEN_ADDRESS=/tmp/nvim"</xhtml:span> .. pane:pane_id() .. <xhtml:span class="hljs-string">" "</xhtml:span> .. wezterm.home_dir .. <xhtml:span class="hljs-string">"/.local/bin/wezterm.nvim.navigator"</xhtml:span> .. <xhtml:span class="hljs-string">" "</xhtml:span> .. direction_nvim)
    <xhtml:span class="hljs-keyword">if</xhtml:span> result <xhtml:span class="hljs-keyword">then</xhtml:span>
                window:perform_action(
            act({ SendString = <xhtml:span class="hljs-string">"\x17"</xhtml:span> .. direction_nvim }),
            pane
        )
    <xhtml:span class="hljs-keyword">else</xhtml:span>
        window:perform_action(
            act({ ActivatePaneDirection = direction_wez }),
            pane
        )
    <xhtml:span class="hljs-keyword">end</xhtml:span>
<xhtml:span class="hljs-keyword">end</xhtml:span>

wezterm.on(<xhtml:span class="hljs-string">"move-left"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Left"</xhtml:span>, <xhtml:span class="hljs-string">"h"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-right"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Right"</xhtml:span>, <xhtml:span class="hljs-string">"l"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-up"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Up"</xhtml:span>, <xhtml:span class="hljs-string">"k"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"move-down"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        move_around(window, pane, <xhtml:span class="hljs-string">"Down"</xhtml:span>, <xhtml:span class="hljs-string">"j"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

<xhtml:span class="hljs-keyword">local</xhtml:span> vim_resize = <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, direction_wez, direction_nvim)</xhtml:span></xhtml:span>
        <xhtml:span class="hljs-keyword">local</xhtml:span> result = <xhtml:span class="hljs-built_in">os</xhtml:span>.<xhtml:span class="hljs-built_in">execute</xhtml:span>(
                <xhtml:span class="hljs-string">"env NVIM_LISTEN_ADDRESS=/tmp/nvim"</xhtml:span>
                        .. pane:pane_id()
                        .. <xhtml:span class="hljs-string">" "</xhtml:span>
            .. wezterm.home_dir
                        .. <xhtml:span class="hljs-string">"/.local/bin/wezterm.nvim.navigator"</xhtml:span>
                        .. <xhtml:span class="hljs-string">" "</xhtml:span>
                        .. direction_nvim
        )
        <xhtml:span class="hljs-keyword">if</xhtml:span> result <xhtml:span class="hljs-keyword">then</xhtml:span>
                window:perform_action(act({ SendString = <xhtml:span class="hljs-string">"\x1b"</xhtml:span> .. direction_nvim }), pane)
        <xhtml:span class="hljs-keyword">else</xhtml:span>
                window:perform_action(act({ ActivatePaneDirection = direction_wez }), pane)
        <xhtml:span class="hljs-keyword">end</xhtml:span>
<xhtml:span class="hljs-keyword">end</xhtml:span>

wezterm.on(<xhtml:span class="hljs-string">"resize-left"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Left"</xhtml:span>, <xhtml:span class="hljs-string">"h"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-right"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Right"</xhtml:span>, <xhtml:span class="hljs-string">"l"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-up"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Up"</xhtml:span>, <xhtml:span class="hljs-string">"k"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

wezterm.on(<xhtml:span class="hljs-string">"resize-down"</xhtml:span>, <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane)</xhtml:span></xhtml:span>
        vim_resize(window, pane, <xhtml:span class="hljs-string">"Down"</xhtml:span>, <xhtml:span class="hljs-string">"j"</xhtml:span>)
<xhtml:span class="hljs-keyword">end</xhtml:span>)

<xhtml:span class="hljs-comment">-- --------------------------------------------------------------------</xhtml:span>
<xhtml:span class="hljs-comment">-- CONFIGURATION</xhtml:span>
<xhtml:span class="hljs-comment">-- --------------------------------------------------------------------</xhtml:span>

<xhtml:span class="hljs-comment">-- This table will hold the configuration.</xhtml:span>
<xhtml:span class="hljs-keyword">local</xhtml:span> <xhtml:span class="hljs-built_in">config</xhtml:span> = {}

<xhtml:span class="hljs-comment">-- In newer versions of wezterm, use the config_builder which will</xhtml:span>
<xhtml:span class="hljs-comment">-- help provide clearer error messages</xhtml:span>
<xhtml:span class="hljs-keyword">if</xhtml:span> wezterm.config_builder <xhtml:span class="hljs-keyword">then</xhtml:span>
  <xhtml:span class="hljs-built_in">config</xhtml:span> = wezterm.config_builder()
<xhtml:span class="hljs-keyword">end</xhtml:span>

<xhtml:span class="hljs-built_in">config</xhtml:span>.adjust_window_size_when_changing_font_size = <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.automatically_reload_config = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.color_scheme = <xhtml:span class="hljs-string">'Solarized (dark) (terminal.sexy)'</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.enable_scroll_bar = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.enable_wayland = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-comment">-- config.font = wezterm.font('Hack')</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.font = wezterm.font(<xhtml:span class="hljs-string">'Monaspace Neon'</xhtml:span>)
<xhtml:span class="hljs-built_in">config</xhtml:span>.font_size = <xhtml:span class="hljs-number">12.0</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.hide_tab_bar_if_only_one_tab = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-comment">-- The leader is similar to how tmux defines a set of keys to hit in order to</xhtml:span>
<xhtml:span class="hljs-comment">-- invoke tmux bindings. Binding to ctrl-a here to mimic tmux</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.leader = { key = <xhtml:span class="hljs-string">'a'</xhtml:span>, mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>, timeout_milliseconds = <xhtml:span class="hljs-number">2000</xhtml:span> }
<xhtml:span class="hljs-built_in">config</xhtml:span>.mouse_bindings = {
    <xhtml:span class="hljs-comment">-- Open URLs with Ctrl+Click</xhtml:span>
    {
        event = { Up = { streak = <xhtml:span class="hljs-number">1</xhtml:span>, button = <xhtml:span class="hljs-string">'Left'</xhtml:span> } },
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act.OpenLinkAtMouseCursor,
    }
}
<xhtml:span class="hljs-built_in">config</xhtml:span>.pane_focus_follows_mouse = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.scrollback_lines = <xhtml:span class="hljs-number">5000</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.use_dead_keys = <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.warn_about_missing_glyphs = <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.window_decorations = <xhtml:span class="hljs-string">'TITLE | RESIZE'</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.window_padding = {
    left = <xhtml:span class="hljs-number">0</xhtml:span>,
    right = <xhtml:span class="hljs-number">0</xhtml:span>,
    top = <xhtml:span class="hljs-number">0</xhtml:span>,
    bottom = <xhtml:span class="hljs-number">0</xhtml:span>,
}

<xhtml:span class="hljs-comment">-- Tab bar</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.use_fancy_tab_bar = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.tab_bar_at_bottom = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.switch_to_last_active_tab_when_closing_tab = <xhtml:span class="hljs-literal">true</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.tab_max_width = <xhtml:span class="hljs-number">32</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.colors = {
    tab_bar = {
        active_tab = {
            fg_color = <xhtml:span class="hljs-string">'#073642'</xhtml:span>,
            bg_color = <xhtml:span class="hljs-string">'#2aa198'</xhtml:span>,
        }
    }
}

<xhtml:span class="hljs-comment">-- Setup muxing by default</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.unix_domains = {
  {
    name = <xhtml:span class="hljs-string">'unix'</xhtml:span>,
  },
}

<xhtml:span class="hljs-comment">-- Custom key bindings</xhtml:span>
<xhtml:span class="hljs-built_in">config</xhtml:span>.keys = {
    <xhtml:span class="hljs-comment">-- -- Disable Alt-Enter combination (already used in tmux to split pane)</xhtml:span>
    <xhtml:span class="hljs-comment">-- {</xhtml:span>
    <xhtml:span class="hljs-comment">--     key = 'Enter',</xhtml:span>
    <xhtml:span class="hljs-comment">--     mods = 'ALT',</xhtml:span>
    <xhtml:span class="hljs-comment">--     action = act.DisableDefaultAssignment,</xhtml:span>
    <xhtml:span class="hljs-comment">-- },</xhtml:span>

    <xhtml:span class="hljs-comment">-- Copy mode</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'['</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ActivateCopyMode,
    },

    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>
    <xhtml:span class="hljs-comment">-- TABS</xhtml:span>
    <xhtml:span class="hljs-comment">--</xhtml:span>
    <xhtml:span class="hljs-comment">-- Where possible, I'm using the same combinations as I would in tmux</xhtml:span>
    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>

    <xhtml:span class="hljs-comment">-- Show tab navigator; similar to listing panes in tmux</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'w'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ShowTabNavigator,
    },
    <xhtml:span class="hljs-comment">-- Create a tab (alternative to Ctrl-Shift-Tab)</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'c'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.SpawnTab <xhtml:span class="hljs-string">'CurrentPaneDomain'</xhtml:span>,
    },
    <xhtml:span class="hljs-comment">-- Rename current tab; analagous to command in tmux</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">','</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.PromptInputLine {
            description = <xhtml:span class="hljs-string">'Enter new name for tab'</xhtml:span>,
            action = wezterm.action_callback(
                <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, line)</xhtml:span></xhtml:span>
                    <xhtml:span class="hljs-keyword">if</xhtml:span> line <xhtml:span class="hljs-keyword">then</xhtml:span>
                        window:active_tab():set_title(line)
                    <xhtml:span class="hljs-keyword">end</xhtml:span>
                <xhtml:span class="hljs-keyword">end</xhtml:span>
            ),
        },
    },
    <xhtml:span class="hljs-comment">-- Move to next/previous TAB</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'n'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ActivateTabRelative(<xhtml:span class="hljs-number">1</xhtml:span>),
    },
    {
        key = <xhtml:span class="hljs-string">'p'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ActivateTabRelative(<xhtml:span class="hljs-number">-1</xhtml:span>),
    },
    <xhtml:span class="hljs-comment">-- Close tab</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'&amp;'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act.CloseCurrentTab{ confirm = <xhtml:span class="hljs-literal">true</xhtml:span> },
    },

    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>
    <xhtml:span class="hljs-comment">-- PANES</xhtml:span>
    <xhtml:span class="hljs-comment">--</xhtml:span>
    <xhtml:span class="hljs-comment">-- These are great and get me most of the way to replacing tmux</xhtml:span>
    <xhtml:span class="hljs-comment">-- entirely, particularly as you can use "wezterm ssh" to ssh to another</xhtml:span>
    <xhtml:span class="hljs-comment">-- server, and still retain Wezterm as your terminal there.</xhtml:span>
    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>

    <xhtml:span class="hljs-comment">-- -- Vertical split</xhtml:span>
    {
        <xhtml:span class="hljs-comment">-- |</xhtml:span>
        key = <xhtml:span class="hljs-string">'|'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act.SplitPane {
            direction = <xhtml:span class="hljs-string">'Right'</xhtml:span>,
            size = { Percent = <xhtml:span class="hljs-number">50</xhtml:span> },
        },
    },
    <xhtml:span class="hljs-comment">-- Horizontal split</xhtml:span>
    {
        <xhtml:span class="hljs-comment">-- -</xhtml:span>
        key = <xhtml:span class="hljs-string">'-'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.SplitPane {
            direction = <xhtml:span class="hljs-string">'Down'</xhtml:span>,
            size = { Percent = <xhtml:span class="hljs-number">50</xhtml:span> },
        },
    },
    <xhtml:span class="hljs-comment">-- CTRL + (h,j,k,l) to move between panes</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'h'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-left"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'j'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-down"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'k'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-up"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'l'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'CTRL'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"move-right"</xhtml:span> }),
    },
    <xhtml:span class="hljs-comment">-- ALT + (h,j,k,l) to resize panes</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'h'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-left"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'j'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-down"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'k'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-up"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'l'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"resize-right"</xhtml:span> }),
    },
    <xhtml:span class="hljs-comment">-- Close/kill active pane</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'x'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.CloseCurrentPane { confirm = <xhtml:span class="hljs-literal">true</xhtml:span> },
    },
    <xhtml:span class="hljs-comment">-- Swap active pane with another one</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'{'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act.PaneSelect { mode = <xhtml:span class="hljs-string">"SwapWithActiveKeepFocus"</xhtml:span> },
    },
    <xhtml:span class="hljs-comment">-- Zoom current pane (toggle)</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'z'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.TogglePaneZoomState,
    },
    {
        key = <xhtml:span class="hljs-string">'f'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'ALT'</xhtml:span>,
        action = act.TogglePaneZoomState,
    },
    <xhtml:span class="hljs-comment">-- Move to next/previous pane</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">';'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ActivatePaneDirection(<xhtml:span class="hljs-string">'Prev'</xhtml:span>),
    },
    {
        key = <xhtml:span class="hljs-string">'o'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ActivatePaneDirection(<xhtml:span class="hljs-string">'Next'</xhtml:span>),
    },

    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>
    <xhtml:span class="hljs-comment">-- Workspaces</xhtml:span>
    <xhtml:span class="hljs-comment">--</xhtml:span>
    <xhtml:span class="hljs-comment">-- These are roughly equivalent to tmux sessions.</xhtml:span>
    <xhtml:span class="hljs-comment">-- ----------------------------------------------------------------</xhtml:span>

    <xhtml:span class="hljs-comment">-- Attach to muxer</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'a'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.AttachDomain <xhtml:span class="hljs-string">'unix'</xhtml:span>,
    },

    <xhtml:span class="hljs-comment">-- Detach from muxer</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'d'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.DetachDomain { DomainName = <xhtml:span class="hljs-string">'unix'</xhtml:span> },
    },

    <xhtml:span class="hljs-comment">-- Show list of workspaces</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'s'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER'</xhtml:span>,
        action = act.ShowLauncherArgs { flags = <xhtml:span class="hljs-string">'WORKSPACES'</xhtml:span> },
    },
    <xhtml:span class="hljs-comment">-- Rename current session; analagous to command in tmux</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'$'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act.PromptInputLine {
            description = <xhtml:span class="hljs-string">'Enter new name for session'</xhtml:span>,
            action = wezterm.action_callback(
                <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span><xhtml:span class="hljs-params">(window, pane, line)</xhtml:span></xhtml:span>
                    <xhtml:span class="hljs-keyword">if</xhtml:span> line <xhtml:span class="hljs-keyword">then</xhtml:span>
                        mux.rename_workspace(
                            window:mux_window():get_workspace(),
                            line
                        )
                    <xhtml:span class="hljs-keyword">end</xhtml:span>
                <xhtml:span class="hljs-keyword">end</xhtml:span>
            ),
        },
    },

    <xhtml:span class="hljs-comment">-- Session manager bindings</xhtml:span>
    {
        key = <xhtml:span class="hljs-string">'s'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"save_session"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'L'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"load_session"</xhtml:span> }),
    },
    {
        key = <xhtml:span class="hljs-string">'R'</xhtml:span>,
        mods = <xhtml:span class="hljs-string">'LEADER|SHIFT'</xhtml:span>,
        action = act({ EmitEvent = <xhtml:span class="hljs-string">"restore_session"</xhtml:span> }),
    },
}

<xhtml:span class="hljs-comment">-- and finally, return the configuration to wezterm</xhtml:span>
<xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-built_in">config</xhtml:span>
</xhtml:code></xhtml:pre></xhtml:details>
<xhtml:h2>Closing</xhtml:h2>
<xhtml:p>I've been really impressed by Wezterm! One thing that's
absolutely magical is that I don't ever have to think about whether
or not I've started <xhtml:code>tmux</xhtml:code>; I can just start splitting
the window into panes on the fly as needed. On top of that, having
the configuration be a limited programming language, and one that
is NOT specific to Wezterm, means that I can (a) use a skill I
already have, and (b) do some limited programming of terminal
behavior, which allows me to customize it for my own use cases.</xhtml:p>
<xhtml:p>Would I recommend Wezterm to others? Absolutely! One reason I
was excited to try it is so I could use a terminal I could
potentially port <xhtml:em>elsewhere</xhtml:em>; Wezterm works on Linux,
obviously, but also Windows and Mac, making it a great
cross-platform replacement for whatever native terminals you were
using previously. This can be hugely useful if you switch between
systems regularly, or even if you are contemplating a switch in the
future and want to make your landing as soft as possible.</xhtml:p>
<xhtml:p>Thanks, Wez, for this great software, and a huge thank you to
your community and contributors as well!</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2024-07-04-how-i-use-wezterm.html">How I use
Wezterm</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2024-07-04T13:50:46-05:00">4 July 2024</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
</feed>
