A Weird Imagination

Client/server over named pipes

Posted in

The problem#

Firefox Marionette only allows a single client to connect a time, so I'd like to have a program in charge of holding that connection that can communicate with the other parts of the system that know what I want Firefox to actually do. While a common way of handling this is to run an HTTP server, that seems pretty heavyweight and would allow access to any user on the machine.

This can be generalized to any case where we want to be able to control one program from another on the same computer. Some reasons why we might not be able to simply act from the first program is if the latter has different access permissions or is holding onto some state like, as mentioned, an open socket.

The solution#

The two programs can communicate over a named pipe, also known as a FIFO. The mkfifo command creates one on the filesystem:

$ mkfifo a_pipe
$ chown server_uid:clients_gid a_pipe
$ chmod 620 a_pipe

Then you can use tail -f a_pipe to watch the pipe and echo something > pipe to write to the pipe. To add a bit more structure, here's a very simple server and client in Python where the client sends one command per line as JSON and the server processes the commands one at a time:

# server.py
import fileinput
import json

def process_cmd(cmd, *args):
    print(f"In process_cmd({cmd})...")

for line in fileinput.input():
    try:
        process_cmd(*json.loads(line))
    except Exception as ex:
        print("Command failed:")
        print(ex, flush=True)
# client.py
import json
import sys

print(json.dumps(sys.argv[1:]))
# Run the server.
$ tail -f a_pipe | python server.py
# Send some commands using the client.
$ python client.py cmd foo bar > a_pipe
$ python client.py another_cmd baz > a_pipe

Then the server will print out

In process_cmd(cmd)...
In process_cmd(another_cmd)...

The details#

Read more…

Borderless browser window

The problem#

Web browser UIs have a lot more than just displaying the web page, which is useful when using them as a browser, but clutters the screen if all we want is to define what is displayed on part of the screen using HTML. So, can we get Firefox into a mode where it really does show just the website and nothing else? Firefox does have a fullscreen mode that does that, but it covers an entire monitor.

The solution#

To hide all of the Firefox menus and toolbars, put the following in the chrome/userChrome.css file under your Firefox profile directory (you will likely want to create a separate profile from the one you use for web browsing):

#TabsToolbar, #TabsToolbar-customization-target,
#nav-bar, #urlbar-container, #searchbar {
  visibility: collapse !important;
}

To hide the window border and titlebar, compile toggle-decorations.c and run

firefox &
./toggle-decorations $(xdotool selectwindow)

and then click on the Firefox window once it opens. It may be easier to bind it to a hotkey with xdotool getactivewindow or use some other way to identify the window.

The details#

Read more…

Fullscreen mode on part of screen

Posted in

The problem#

Many applications have a fullscreen mode that has a different interface from their windowed mode. For example, many media players will show just the video in fullscreen mode but include media controls in windowed mode. But, especially if you have a large monitor, you may want to use that interface while only having the application take up part of your monitor.

The solution#

I could not find a solution that works on every window manager.

The window manager handles resizing the application when it switches to fullscreen, so the most straightforward way to accomplish this is to not run a window manager. Problem solved! Unfortunately, window managers are really useful, so outside of some niche cases where you're positioning windows with xdotool, that's probably not what you want.

There's a "fakefullscreen" option in some forks of the very configurable window manager dwm: base dwm with the fakefullscreen patch always does fullscreen that way, the instantWM fork has a hotkey Super+Shift+F that toggles fake fullscreen for a window, and awesome can be configured to do the same.

For more common window managers, there is a solution, but more than two virtual monitors requires an xorg-server newer than 21.1.10 (which is the most recent release at time of writing, so you would have to compile it yourself), and in my tests, it only worked on Compiz, and not Mutter, KWin, or Xfwm. Use xrandr 1.5+ to define virtual monitors on sections of your monitors and then maximizing or fullscreening applications should respect those boundaries:

xrandr --setmonitor lefthalf 960/217x1080/132+0+0 LVDS-1
# This is a hack, should be "LVDS-1", not "none".
xrandr --setmonitor righthalf 960/217x1080/132+960+0 none

where the the geometery specification format is w/mmwxh/mmh+x+y (mmw/h="millimeters width/height") and LVDS-1 is the name xrandr gives to my physical monitor. Note that xorg-server 21.1.10 and older have a limit of one virtual monitor per physical monitor which we can circumvent by putting the second virtual monitor on "none".

The details#

Read more…

Reacting to active window

Posted in

The problem#

Which window I have focused is a signal to the computer for the state I want it to be in. For instance, I normally leave my speaker muted so, for example, I don't accidentally play sound from a website with unexpected videos. But this means that when I do want sound, I need to manually unmute the sound, even though I've already told the computer that I want to watch Netflix, which always involves turning on the sound.

Of course, for the particular problem of unmuting the sound, adding a keyboard shortcut and rereading xkcd 1205: Is It Worth the Time? probably would have been a more appropriate solution. But I wanted a general solution to the problem.

The solution#

Download x11_watch_active_window.py. Then the following script will unmute the speakers if Netflix is focused:

#!/bin/sh
x11_watch_active_window.py | while read -r FocusApp
do
    if [ "Netflix - Google Chrome" = "$FocusApp" ]
    then
        echo Netflix is focused, unmuting.
        pactl set-sink-mute 0 0
    fi
done

The details#

Read more…