From 15594f655ab7296799bfffed1e795a8d11bcdea7 Mon Sep 17 00:00:00 2001 From: M Naufal Shidqi Date: Sat, 28 Sep 2024 01:09:57 +0700 Subject: [PATCH 1/3] [window_switcher:0.5.0] --- window_switcher/__init__.py | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 window_switcher/__init__.py diff --git a/window_switcher/__init__.py b/window_switcher/__init__.py new file mode 100644 index 00000000..9c1069b1 --- /dev/null +++ b/window_switcher/__init__.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +"""List and manage X11 windows. + +Synopsis: """ + +# Copyright (c) 2022 Manuel Schneider + +import subprocess +from collections import namedtuple +from albert import * + +md_iid = '2.3' +md_version = "0.5.0" +md_name = "Window Switcher" +md_description = "Switch Window" +md_license = "MIT" +md_url = "https://github.com/albertlauncher/python/tree/main/window_switcher" +md_bin_dependencies = "wmctrl" +md_authors = ["Ed Perez", "Manuel S.", "dshoreman", "nopsqi"] + +Window = namedtuple("Window", ["wid", "desktop", "wm_class", "host", "wm_name"]) + + +class Plugin(PluginInstance, TriggerQueryHandler): + def __init__(self): + PluginInstance.__init__(self) + TriggerQueryHandler.__init__( + self, self.id, self.name, self.description, + defaultTrigger='w ' + ) + + def handleTriggerQuery(self, query): + for line in subprocess.check_output(['wmctrl', '-l', '-x']).splitlines(): + win = Window(*parseWindow(line)) + + if win.desktop == "-1": + continue + + win_instance, win_class = win.wm_class.replace(' ', '-').split('.', 1) + + m = Matcher(query.string) + if not query.string or m.match(win_instance + ' ' + win_class + ' ' + win.wm_name): + query.add(StandardItem( + id="%s%s" % (md_name, win.wm_class), + iconUrls=["xdg:%s" % win_instance], + text="%s - Desktop %s" % (win_class.replace('-', ' '), win.desktop), + subtext=win.wm_name, + actions=[Action("switch", + "Switch Window", + lambda w=win: runDetachedProcess(["wmctrl", '-i', '-a', w.wid])), + Action("move", + "Move window to this desktop", + lambda w=win: runDetachedProcess(["wmctrl", '-i', '-R', w.wid])), + Action("close", + "Close the window gracefully.", + lambda w=win: runDetachedProcess(["wmctrl", '-c', w.wid]))] + )) + + +def parseWindow(line): + win_id, desktop, rest = line.decode().split(None, 2) + win_class, rest = rest.split(' ', 1) + host, title = rest.strip().split(None, 1) + + return [win_id, desktop, win_class, host, title] From f1bb22083ebce29be1c84b3214e7fe0cc9d89e1e Mon Sep 17 00:00:00 2001 From: M Naufal Shidqi Date: Thu, 27 Mar 2025 23:47:43 +0700 Subject: [PATCH 2/3] [x_window_switcher] rename module name --- {window_switcher => x_window_switcher}/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {window_switcher => x_window_switcher}/__init__.py (100%) diff --git a/window_switcher/__init__.py b/x_window_switcher/__init__.py similarity index 100% rename from window_switcher/__init__.py rename to x_window_switcher/__init__.py From b5202d82eea075d3ab2ed042cf4b236f4d6eb4f3 Mon Sep 17 00:00:00 2001 From: M Naufal Shidqi Date: Fri, 28 Mar 2025 00:06:11 +0700 Subject: [PATCH 3/3] [x_window_switcher] v3 --- x_window_switcher/__init__.py | 82 +++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/x_window_switcher/__init__.py b/x_window_switcher/__init__.py index 9c1069b1..cc4cfe59 100644 --- a/x_window_switcher/__init__.py +++ b/x_window_switcher/__init__.py @@ -1,21 +1,15 @@ # -*- coding: utf-8 -*- -"""List and manage X11 windows. - -Synopsis: """ - -# Copyright (c) 2022 Manuel Schneider - import subprocess from collections import namedtuple from albert import * -md_iid = '2.3' -md_version = "0.5.0" -md_name = "Window Switcher" -md_description = "Switch Window" +md_iid = "3.0" +md_version = "0.6.0" +md_name = "X Window Switcher" +md_description = "Switch X11 Windows" md_license = "MIT" -md_url = "https://github.com/albertlauncher/python/tree/main/window_switcher" +md_url = "https://github.com/albertlauncher/python/tree/main/x_window_switcher" md_bin_dependencies = "wmctrl" md_authors = ["Ed Perez", "Manuel S.", "dshoreman", "nopsqi"] @@ -25,37 +19,51 @@ class Plugin(PluginInstance, TriggerQueryHandler): def __init__(self): PluginInstance.__init__(self) - TriggerQueryHandler.__init__( - self, self.id, self.name, self.description, - defaultTrigger='w ' - ) + TriggerQueryHandler.__init__(self) + + # Check for X session and wmctrl availability + try: + subprocess.check_call(["wmctrl", "-m"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except FileNotFoundError: + raise Exception("wmctrl not found. Please install wmctrl.") + except subprocess.CalledProcessError: + raise Exception("Unable to communicate with X11 window manager. This plugin requires a running X session.") + + def defaultTrigger(self): + return 'w ' + + def synopsis(self, query): + return "" def handleTriggerQuery(self, query): - for line in subprocess.check_output(['wmctrl', '-l', '-x']).splitlines(): - win = Window(*parseWindow(line)) + try: + for line in subprocess.check_output(['wmctrl', '-l', '-x']).splitlines(): + win = Window(*parseWindow(line)) - if win.desktop == "-1": - continue + if win.desktop == "-1": + continue - win_instance, win_class = win.wm_class.replace(' ', '-').split('.', 1) + win_instance, win_class = win.wm_class.replace(' ', '-').split('.', 1) - m = Matcher(query.string) - if not query.string or m.match(win_instance + ' ' + win_class + ' ' + win.wm_name): - query.add(StandardItem( - id="%s%s" % (md_name, win.wm_class), - iconUrls=["xdg:%s" % win_instance], - text="%s - Desktop %s" % (win_class.replace('-', ' '), win.desktop), - subtext=win.wm_name, - actions=[Action("switch", - "Switch Window", - lambda w=win: runDetachedProcess(["wmctrl", '-i', '-a', w.wid])), - Action("move", - "Move window to this desktop", - lambda w=win: runDetachedProcess(["wmctrl", '-i', '-R', w.wid])), - Action("close", - "Close the window gracefully.", - lambda w=win: runDetachedProcess(["wmctrl", '-c', w.wid]))] - )) + m = Matcher(query.string) + if not query.string or m.match(win_instance + ' ' + win_class + ' ' + win.wm_name): + query.add(StandardItem( + id="%s%s" % (md_name, win.wm_class), + iconUrls=["xdg:%s" % win_instance], + text="%s - Desktop %s" % (win_class.replace('-', ' '), win.desktop), + subtext=win.wm_name, + actions=[Action("switch", + "Switch Window", + lambda w=win: runDetachedProcess(["wmctrl", '-i', '-a', w.wid])), + Action("move", + "Move window to this desktop", + lambda w=win: runDetachedProcess(["wmctrl", '-i', '-R', w.wid])), + Action("close", + "Close the window gracefully.", + lambda w=win: runDetachedProcess(["wmctrl", '-c', w.wid]))] + )) + except subprocess.CalledProcessError as e: + warning(f"Error executing wmctrl: {str(e)}") def parseWindow(line):