Skip to content

Instantly share code, notes, and snippets.

@crazygolem
Created March 23, 2023 09:14
Show Gist options
  • Save crazygolem/b3021cd834f013145afa54805ef330b4 to your computer and use it in GitHub Desktop.
Save crazygolem/b3021cd834f013145afa54805ef330b4 to your computer and use it in GitHub Desktop.
Launch an application with KDocker from its desktop file.
#!/usr/bin/env python3
import argparse
import os
import re
import shlex
import shutil
import subprocess
# pyxdg; https://freedesktop.org/wiki/Software/pyxdg/
from xdg.DesktopEntry import DesktopEntry
from xdg import IconTheme
for bin in ['kdocker', 'xdotool']:
if not shutil.which(bin):
raise Exception(f"Missing dependency, could not find executable: {bin}")
def file(path):
if os.path.isfile(path):
return path
else:
raise Exception('Could not find specified desktop file')
parser = argparse.ArgumentParser(
description='Launch an application with KDocker from its desktop file.')
parser.add_argument('-d', '--avoid-launch', action='store_true',
help="dock an existing window if possible")
parser.add_argument('-D', '--dock-only', action='store_true',
help="dock an existing window instead of launching the app")
parser.add_argument('-c', '--click', action='store_true',
help="select the window to dock by clicking on it")
parser.add_argument('-n', '--name', action='store_true',
help="use the name/title instead of WMClass to match the window")
parser.add_argument('path', type=file, help='Path to a desktop file')
args = parser.parse_args()
def find_window(de, wait=True):
# I could not make KDocker match the windows' title with '-n', and I'm not
# sure why...
# So instead the matching is done manually and the result is fed to KDocker.
# This adds a dependency to an external program, but also allows matching on
# other attributes, like the WMClass which seems to be more robust.
wmclass = de.getStartupWMClass()
appname = de.getName()
cmd = ['xdotool', 'search', '--onlyvisible', '--limit', '1']
if wait:
cmd += ['--sync']
if wmclass and not args.name:
cmd += ['--classname', re.escape(wmclass)]
else:
cmd += ['--name', re.escape(appname)]
return subprocess.run(
cmd,
check=wait, timeout=5,
capture_output=True, text=True
).stdout.strip()
de = DesktopEntry()
de.parse(args.path)
kdargs = []
if icon := de.getIcon():
# The Icon value can be either already an absolute path, or a name which
# needs to be looked up.
icon = IconTheme.getIconPath(icon)
kdargs += ['-i', icon]
if not (args.dock_only or (args.avoid_launch and find_window(de, wait=False))):
# KDocker is not able to target correctly windows of apps that launch
# separate processes, or processes that own multiple windows that should be
# docked independently.
# PWAs installed with Chrome can fall into both categories, as chrome reuses
# the first process that was launched for subsequent windows, leading to a
# confusing situation where the first PWA to launch is correctly docked as
# long as there is no existing Chrome window, but the following PWAs aren't.
# To work around this, the app is first launched "in the background", then
# KDocker is called separately to dock the window.
subprocess.Popen(shlex.split(de.getExec()))
if not args.click:
kdargs += ['-w', find_window(de)]
try:
subprocess.run(['kdocker', *kdargs], check=True)
except KeyboardInterrupt:
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment