Published: Tue 19 April 2016
By Doug Christman
In zsh .
There are a handful of keys that I never touch—Scroll Lock, SysRq, F1 through F12, and that strange button with an unidentifiable glyph which doesn't seem to do anything.
Pause is another one of those keys. Wouldn't it be nice if pause
lived up to its name and could actually pause a video or music, regardless of which window is in focus?
Well, it can. But that's not the interesting part. This post showcases some curious functionality of zsh that ended up being surprisingly useful: the zsocket
command.
the setup
My media player of choice, mpv, can be controlled by an external program through the use of a JSON IPC protocol. Let's get started by enabling the necessary option in mpv.conf
—it just takes a single line:
input-ipc-server=/tmp/mpv-socket
Whenever we run mpv, it will start listening for commands on a Unix domain socket named /tmp/mpv-socket
(creating the file if it doesn't already exist).
The man page for mpv describes its protocol in more detail, but the message for toggling the player's pause state is fairly straightforward:
{"command": ["cycle", "pause"]}
the solution
Now that the socket is open, we need to send our command. Despite the fact that it looks like a normal file, we can't simply echo
a string to the socket:
> echo '{"command": ["cycle", "pause"]}' > /tmp/mpv-socket
zsh: no such device or address: /tmp/mpv-socket
mpv's man page recommends using socat
to talk to the socket. That may be the easiest solution, but I'd rather not install an extra package on my system for a command that I only use in one place.
We could write a short program to do the job, but the POSIX socket API is a bit unwieldy for such a simple task.
Considering that this post is supposed to be about zsh, you already know where this is going. Fine, let's see what zsh can do:
zmodload zsh/net/socket
zsocket /tmp/mpv-socket
print -u "${ REPLY } " '{"command": ["cycle", "pause"]}'
Three lines, not too shabby! It works like this:
zmodload
loads zsh's socket module, which provides the zsocket
function.
zsocket
opens the socket and stores its file descriptor in the variable REPLY.
print
writes our command to the socket. The -u
option allows us to specify a file descriptor.
All that's left is to wire up the pause key to run our script. That process varies by desktop environment, so I'll leave the rest up to you.
One last thing...
If you think zsocket
is neat, be sure to check out ztcp
in the zshmodules
man page. Just as zsocket
provides a simple way to communicate via Unix sockets, ztcp
can be used to read and write Internet sockets.
Happy scripting!