Skip to content

Commit 83ba7a5

Browse files
committed
Add Linux support: Implement wmctrl fallback and update install docs
1 parent 813a2d1 commit 83ba7a5

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ WASP simulates natural mouse activity and clicks to keep those tracking demons t
7878
- Python 3.8+ (the language of rebellion)
7979
- A burning desire for work-life balance
8080

81+
#### Linux Users 🐧
82+
You'll need a few extra tools to make the magic happen:
83+
```bash
84+
sudo apt-get install python3-tk python3-dev wmctrl
85+
```
86+
- `python3-tk`: Required for mouse control features
87+
- `wmctrl`: Required for the window switcher to work
88+
8189
### Installation
8290

8391
1. **Clone Your Freedom**

core/window_switcher.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
import time
33
try:
44
import pygetwindow as gw
5-
except ImportError:
5+
except (ImportError, NotImplementedError):
66
gw = None
7+
import sys
8+
import subprocess
79
from PyQt6.QtCore import QThread, pyqtSignal
810

911
class WindowSwitcher(QThread):
@@ -42,13 +44,32 @@ def set_interval(self, fixed_interval=None, min_interval=None, max_interval=None
4244
if max_interval is not None:
4345
self.max_interval = max(self.min_interval + 10, max_interval)
4446

47+
def _get_windows_linux(self):
48+
"""Get list of windows on Linux using wmctrl"""
49+
try:
50+
# wmctrl -l lists windows: ID Desktop Host Title
51+
output = subprocess.check_output(['wmctrl', '-l'], text=True)
52+
windows = []
53+
for line in output.splitlines():
54+
parts = line.split(maxsplit=3)
55+
if len(parts) > 3:
56+
windows.append(parts[3])
57+
return windows
58+
except (subprocess.SubprocessError, FileNotFoundError):
59+
return []
60+
4561
def get_all_windows(self):
4662
"""Get list of all available windows"""
47-
if gw is None:
63+
if sys.platform.startswith('linux'):
64+
all_windows = self._get_windows_linux()
65+
if not all_windows and gw is None:
66+
return []
67+
elif gw is None:
4868
return []
4969

5070
try:
51-
all_windows = gw.getAllTitles()
71+
if not sys.platform.startswith('linux'):
72+
all_windows = gw.getAllTitles()
5273
# Filter out empty titles and system windows
5374
valid_windows = [w for w in all_windows
5475
if w and w.strip()]
@@ -75,10 +96,15 @@ def switch_to_random_window(self):
7596
try:
7697
# Select random window
7798
target_window = random.choice(windows)
78-
window = gw.getWindowsWithTitle(target_window)[0]
7999

80-
# Activate the window
81-
window.activate()
100+
if sys.platform.startswith('linux'):
101+
# Activate window on Linux
102+
subprocess.run(['wmctrl', '-a', target_window])
103+
else:
104+
window = gw.getWindowsWithTitle(target_window)[0]
105+
106+
# Activate the window
107+
window.activate()
82108

83109
self.switch_counter += 1
84110
self.window_switched.emit(target_window) # Emit the window title
@@ -94,7 +120,7 @@ def stop(self):
94120

95121
def run(self):
96122
"""Main execution loop"""
97-
if gw is None:
123+
if gw is None and not sys.platform.startswith('linux'):
98124
self.status_update.emit("Error: pygetwindow not installed")
99125
return
100126

0 commit comments

Comments
 (0)