Opening a URL in a New Tab of an Existing Browser Window
My search for a good desktop Mastodon client has led me down a path that involved learning some fun ways to interact with existing browser windows on Linux with X programs like xdotool and wmctrl.
Like many people, I've switched from The App Formerly Known As Twitter to Mastodon (where I'm @akkana@fosstodon.org). But the next question was which Mastodon app to use.
I used Twitter via the Bitlbee IRC bridge, so I saw tweets as text only, no images, in my IRC (hexchat) window. To see images, I had to open the URL in Firefox.
My stream on Mastodon is more image-heavy, so I wanted a viewer that shows images while still not taking up much screen space. I looked into a few dedicated Mastodon clients, but most of them seemed optimized for a big window showing multiple things side by side, the opposite of what I wanted. I found the standard Mastodon web client was closer to what I wanted as long as I put it in its own window and resized it to the minimum width.
But there's one problem with that: it means that instead of the single Firefox window I typically keep open, now I have two windows, a large one I use for browsing and a small one for Mastodon. They aren't typically on the same virtual desktop.
If I click on a link in the small Mastodon window, it opens in
that same window. I can middleclick the link to get it to open in a
new tab, but it's a new tab in that undersized window.
I can drag the link from the small window to the large one,
but only if they're on the same desktop, which they typically aren't.
And if I open a link in Firefox from another app, like hexchat,
using firefox -P $profilename -new-tab $url
,
it's unpredictable whether it will open in the small window or the big one.
Firefox doesn't offer a way to specify which window to use.
I could use a different Firefox profile for Mastodon, then change my URL handlers to specify the normal browsing profile, but that means two Firefox instances running at all times.
So I wanted a command that would open a link in a new tab of a normal-sized Firefox window.
That turned out to be surprisingly easy using standard Linux tools like xdotool and wmctrl, using Python to parse their outputs and tie them together.
Find the Right Browser Window
The first step is to find the window ID for a large Firefox window.
You can find window IDs with xlsclients -l
, but
wmctrl -l
gives a more compact form that's easier to parse
(one line per window), and it includes virtual desktop information,
which will be important later.
Update: Ed Davies pointed out that adding -G includes window geometry, so I've rewritten the code below accordingly. Thanks, Ed!
wmctrl -lG
prints lines like
0x014000e9 0 6 70 450 940 charon Home - Fosstodon — Mozilla Firefox 0x0140002c 2 651 211 1124 954 charon Some Page Title- Fosstodon — Mozilla Firefox
Those fields are: window ID, desktop, x, y, width, height, and page title.
So you can find all the Firefox windows with
wmctrl -lG | grep Firefox
.
and get their windowID, current desktop, position and size.
So it's easy to iterate through the Firefox windows and see which ones
are normal sized.
Ideally, I'd like to open the URL in the first Firefox window that's bigger than some threshold, since that's most likely my main browsing window. I think that will be the one with the lowest window ID, but I'm not sure about that; needs more testing. On the other hand, choosing the largest window is very easy so that's what I'm doing initially.
Switch to the Right Desktop
Remember I said I'd need that virtual desktop number from wmctrl? I'm going to load the URL by passing mouse and keyboard events to Firefox, and that requires that the right desktop is active. Fortunately wmctrl can do that: if the window is on desktop 2, switch to it with
wmctrl -s 2
Move the Mouse To Focus the Browser Window
The remaining steps all use xdotool, a wonderful utility that can send mouse and key events to X clients. (I wrote a program many years ago, crikey, to send keyboard events, but these days xdotool can do a lot more than crikey ever could.)
First, focus the window so it will see key presses. I use focus-follows-mouse, so all I need to do is make sure the mouse is over the window:
xdotool mousemove --window windowid winwidth/2 75
I use half the window's width for the X coordinate, figuring that will certainly be inside the urlbar (I don't use a separate search bar; typing searches in the main urlbar works just as well). That 75 is arbitrary and sets the mouse vertically somewhere around the middle of a Firefox window's urlbar. Adjust as needed.
If you use click-to-focus, you'll probably need to add a mouse click after the mouse move:
xdotool mousedown 1 mouseup 1
Open a New Tab
In Firefox, Ctrl-T opens a new tab, so you don't have to fiddle with getting xdotool to click on menu items or anything like that. Just:
xdotool keydown Ctrl keydown t keyup t keyup Ctrl
Type the URL into the urlbar
xdotool type https://blahblah
Hit Enter to send the URL
Firefox doesn't actually go to a URL you type until you hit Enter. I tried adding a newline at the end of the URL, but Firefox just treated it as whitespace. So send the Enter explicitly. xdotool uses Return for that (as do I, most of the time):
xdotool keydown Return keyup Return
Put it all together
I wrapped all these commands in a Python program, using the subprocess module. In Python you have the option of using python-xlib to send the mouse and key events: see my pykey.py for some examples. I haven't looked into how to do the wmctrl parts. But rather than add dependencies on python-xlib and whatever else I needed to replace wmctrl, I decided to stick to using the Linux command-line utilities via subprocess. I called the script openlink.py.
Use a Drop Target
But for Mastodon, copying the URL and middleclicking it is still more steps than I wanted. It would be great if I could just drag it.
Sadly, Openbox doesn't have a way to bind drag-and-drop events. And writing a drop target app turned out to be much harder than the rest of this, resulting in the app I wrote about last week: Qt6 Drop Target qdroptarget.py.
Now I can run
qdroptarget 'droplink %s'
to put a drop target on my desktop. Whenever I drag a link to it,
or middlemouse-paste a link over it,
it finds the largest Firefox window, switches to that desktop
and opens that link in a new tab. It's making Mastodon much more fun
to use.
[ 11:48 Sep 28, 2023 More linux | permalink to this entry | ]