-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgnome_windows_client.py
138 lines (108 loc) · 3.82 KB
/
gnome_windows_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import json
from dataclasses import dataclass
from typing import List, Literal, Optional, Type, TypeVar
from dbus import Interface, SessionBus
T = TypeVar("T")
WorkspaceAndMonitorDirection = Literal["left", "right"]
@dataclass
class MtkRect:
x: int
y: int
width: int
height: int
@dataclass
class Window:
id: int
monitor: int
wm_class: str
wm_class_instance: str
maximized: int
focus: bool
canMove: bool
canResize: bool
canClose: bool
canMaximize: bool
canMinimize: bool
windowArea: MtkRect
currentMonitorWorkArea: MtkRect
allMonitorsWorkArea: MtkRect
in_current_workspace: bool
@dataclass
class PartialWindow:
id: int
monitor: int
focus: bool
in_current_workspace: bool
@dataclass
class FocusedMonitorDetails:
id: int
geometry: MtkRect
class GnomeWindowsExtensionClient:
def __init__(self):
self.bus = SessionBus()
self.proxy = self.bus.get_object(
"org.gnome.Shell", "/org/gnome/Shell/Extensions/WindowCommander"
)
self.interface = Interface(
self.proxy, dbus_interface="org.gnome.Shell.Extensions.WindowCommander"
)
def _parse_response_to_object(self, response: str, obj_type: Type[T]) -> List[T]:
def parse_rects(data: dict):
rect_fields = [
"windowArea",
"currentMonitorWorkArea",
"allMonitorsWorkArea",
"geometry",
]
for field in rect_fields:
if field in data and data[field] is not None:
data[field] = MtkRect(**data[field])
try:
data = json.loads(response)
if isinstance(data, list):
objects = []
for item in data:
parse_rects(item)
objects.append(obj_type(**item))
return objects
# Handle single object
parse_rects(data)
return [obj_type(**data)]
except json.JSONDecodeError as e:
raise Exception(f"Error parsing response: {e}")
def list_windows(self) -> List[PartialWindow]:
response = self.interface.List()
return self._parse_response_to_object(response, PartialWindow)
def get_window_details(self, winid: int) -> Window:
response = self.interface.GetDetails(winid)
windows = self._parse_response_to_object(response, Window)
return windows[0]
def get_focused_monitor_details(self) -> FocusedMonitorDetails:
response = self.interface.GetFocusedMonitorDetails()
return self._parse_response_to_object(response, FocusedMonitorDetails)[0]
# Actions
def move_to_workspace(self, winid: int, direction: WorkspaceAndMonitorDirection):
self.interface.MoveToWorkspace(winid, direction)
def move_to_monitor(self, winid: int, direction: WorkspaceAndMonitorDirection):
self.interface.MoveToMonitor(winid, direction)
def place(self, winid: int, x: int, y: int, width: int, height: int):
self.interface.Place(winid, x, y, width, height)
def move(self, winid: int, x: int, y: int):
self.interface.Move(winid, x, y)
def maximize(self, winid: int):
self.interface.Maximize(winid)
def minimize(self, winid: int):
self.interface.Minimize(winid)
def unmaximize(self, winid: int):
self.interface.Unmaximize(winid)
def activate(self, winid: int):
self.interface.Activate(winid)
def close(self, winid: int):
self.interface.Close(winid)
# Composite actions
def get_focused_window_id(self) -> Optional[int]:
windows = self.list_windows()
focused_window = next((window for window in windows if window.focus), None)
if focused_window is None:
return None
return focused_window.id