Skip to content

Commit c577502

Browse files
Improved doc and fixed memory leak
1 parent 8f9dcaf commit c577502

File tree

7 files changed

+199
-34
lines changed

7 files changed

+199
-34
lines changed

display_server_interactions/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from platform import system as __system
55

6-
__version__ = "0.0.dev1"
6+
__version__ = "0.0.dev2"
77

88
__os_name = __system().lower()
99

@@ -17,6 +17,6 @@
1717
raise NotImplementedError("MacOS is not yet implemented.")
1818

1919
else:
20-
raise NotImplementedError("Your OS is not supported.")
20+
raise Exception("Your OS is not supported.")
2121

2222
__all__ = ["DSI"]

display_server_interactions/base.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,33 @@
33

44
from abc import ABCMeta, abstractmethod
55
from .window import WindowBase
6+
from . import __os_name
67

78

89
class DSIBase(object, metaclass=ABCMeta):
910
@staticmethod
1011
@abstractmethod
1112
def get_active_window() -> WindowBase:
13+
"""
14+
Gets the active window.
15+
Returns None if no window is active.
16+
"""
1217
pass
1318

1419
@staticmethod
1520
@abstractmethod
1621
def get_all_windows() -> list:
1722
"""
18-
Returns: list: List of WindowBase objects.
23+
Returns a list of all Windows.
1924
"""
2025
pass
2126

2227
@classmethod
2328
def get_window_by_pid(cls, pid: int) -> WindowBase:
29+
"""
30+
Get window by pid.
31+
Returns None if no window found.
32+
"""
2433
all_window = cls.get_all_windows()
2534

2635
for window in all_window:
@@ -29,8 +38,40 @@ def get_window_by_pid(cls, pid: int) -> WindowBase:
2938

3039
@classmethod
3140
def get_window_by_name(cls, name: str) -> WindowBase:
41+
"""
42+
Get a window by name.
43+
Returns None if no window with that name is found.
44+
"""
3245
all_window = cls.get_all_windows()
3346

3447
for window in all_window:
3548
if window.name is not None and name in window.name:
3649
return window
50+
51+
@property
52+
def platform(self) -> str:
53+
"""
54+
Returns the platform name.
55+
"""
56+
return __os_name
57+
58+
@property
59+
def linux(self) -> bool:
60+
"""
61+
Returns True if the platform is linux.
62+
"""
63+
return self.platform == "linux"
64+
65+
@property
66+
def windows(self) -> bool:
67+
"""
68+
Returns True if the platform is windows.
69+
"""
70+
return self.platform == "windows"
71+
72+
@property
73+
def mac(self) -> bool:
74+
"""
75+
Returns True if the platform is mac.
76+
"""
77+
return self.platform == "darwin"

display_server_interactions/image.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
# TODO: add way to get pixel from image
44

55
class Image(object):
6+
"""
7+
A class to that holds the raw data of an image.
8+
Use np.array(Image) to get a numpy array of the image.
9+
"""
10+
611
def __init__(self, data, width, height):
712
self.data = data
813
self.width = width

display_server_interactions/linux.py

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/python3
22
# -*- coding: utf-8 -*-
33

4-
from operator import le
5-
4+
# local modules
65
from .base import DSIBase
76
from .window import WindowBase
87
from .image import Image
98

109
# built-in modules
10+
import logging
1111
import ctypes.util
1212
from ctypes import (
1313
POINTER,
@@ -36,10 +36,18 @@
3636

3737
# Setup Xlib Structures
3838
class Display(Structure):
39-
pass
39+
"""
40+
https://tronche.com/gui/x/xlib/display/opening.html#Display\n
41+
/usr/include/X11/Xlib.h: 487
42+
"""
4043

4144

4245
class XImage(Structure):
46+
"""
47+
https://tronche.com/gui/x/xlib/graphics/images.html#XImage\n
48+
/usr/include/X11/Xlib.h: 360-394
49+
"""
50+
4351
_fields_ = [
4452
('width', c_int),
4553
('height', c_int),
@@ -60,6 +68,11 @@ class XImage(Structure):
6068

6169

6270
class XWindowAttributes(Structure):
71+
"""
72+
https://tronche.com/gui/x/xlib/window-information/XGetWindowAttributes.html\n
73+
/usr/include/X11/Xlib.h: 308-334
74+
"""
75+
6376
_fields_ = [
6477
("x", c_int32),
6578
("y", c_int32),
@@ -88,6 +101,11 @@ class XWindowAttributes(Structure):
88101

89102

90103
class XKeyEvent(Structure):
104+
"""
105+
https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html#XKeyEvent\n
106+
/usr/include/X11/Xlib.h: 557-571
107+
"""
108+
91109
_fields_ = [
92110
('type', c_int),
93111
('serial', c_ulong),
@@ -108,6 +126,11 @@ class XKeyEvent(Structure):
108126

109127

110128
class XButtonEvent(ctypes.Structure):
129+
"""
130+
https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html#XButtonEvent\n
131+
/usr/include/X11/Xlib.h: 575-589
132+
"""
133+
111134
_fields_ = [
112135
('type', ctypes.c_int),
113136
('serial', ctypes.c_ulong),
@@ -128,17 +151,68 @@ class XButtonEvent(ctypes.Structure):
128151

129152

130153
class XEvent(ctypes.Union):
154+
"""
155+
https://tronche.com/gui/x/xlib/events/structures.html#XEvent\n
156+
/usr/include/X11/Xlib.h: 973-1009
157+
"""
131158
_fields_ = [
132159
('type', ctypes.c_int),
133160
('xkey', XKeyEvent),
134161
('xbutton', XButtonEvent),
135162
('pad', ctypes.c_long*24),
136163
]
137164

165+
166+
class XErrorEvent(Structure):
167+
"""
168+
https://tronche.com/gui/x/xlib/event-handling/protocol-errors/default-handlers.html#XErrorEvent\n
169+
/usr/include/X11/Xlib.h: 924-932
170+
"""
171+
172+
def __repr__(self) -> str:
173+
return f"XErrorEvent(type={self.type}, serial={self.serial}, error_code={self.error_code}, request_code={self.request_code}, minor_code={self.minor_code})"
174+
175+
_fields_ = [
176+
("type", c_int),
177+
("display", POINTER(Display)),
178+
("serial", c_ulong),
179+
("error_code", c_ubyte),
180+
("request_code", c_ubyte),
181+
("minor_code", c_ubyte),
182+
("resourceid", c_void_p),
183+
]
184+
185+
186+
logger = logging.getLogger(__name__)
187+
logger.setLevel(logging.CRITICAL)
188+
189+
190+
@ctypes.CFUNCTYPE(c_int, POINTER(Display), POINTER(XErrorEvent))
191+
def error_handler(_, event):
192+
logger.error("%s", event.contents)
193+
return 0
194+
195+
196+
xlib.XSetErrorHandler(error_handler)
197+
198+
199+
def get_logger() -> logging.Logger:
200+
"""
201+
Returns a logger that is responsible for logging XErrorEvents.
202+
The logger is set to CRITICAL. So it will not log anything.
203+
To make it log something, set the log level at least to ERROR.
204+
You can archive this with "logger.setLevel(logging.ERROR)".
205+
"""
206+
return logger
207+
138208
# Setup Xlib Variables
139209

140210

141211
class Masks(object):
212+
"""
213+
https://tronche.com/gui/x/xlib/events/mask.html\n
214+
/usr/include/X11/X.h: 150-175
215+
"""
142216
NoEventMask = 0
143217
KeyPressMask = 1
144218
KeyReleaseMask = 2
@@ -168,6 +242,10 @@ class Masks(object):
168242

169243

170244
class EventTypes(object):
245+
"""
246+
https://tronche.com/gui/x/xlib/events/types.html\n
247+
/usr/include/X11/X.h: 181-215
248+
"""
171249
KeyPress = 2
172250
KeyRelease = 3
173251
ButtonPress = 4
@@ -206,6 +284,10 @@ class EventTypes(object):
206284

207285

208286
class KeyMasks(object):
287+
"""
288+
https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html\n
289+
/usr/include/X11/X.h: 221-228
290+
"""
209291
ShiftMask = 1
210292
LockMask = 2
211293
ControlMask = 4
@@ -217,6 +299,10 @@ class KeyMasks(object):
217299

218300

219301
class ButtonCodes(object):
302+
"""
303+
https://tronche.com/gui/x/xlib/events/keyboard-pointer/keyboard-pointer.html\n
304+
/usr/include/X11/X.h: 259-263
305+
"""
220306
AnyButton = 0
221307
Button1 = 1
222308
Button2 = 2
@@ -265,13 +351,15 @@ class ButtonCodes(object):
265351

266352

267353
def get_window_property(window_xid: int, property: str, type: _SimpleCData):
354+
"""
355+
https://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html
356+
"""
268357
actual_type_return = c_ulong()
269358
actual_format_return = c_int()
270359
nitems_return = c_ulong()
271360
bytes_after_return = c_ulong()
272361
prop_return = POINTER(c_ubyte)()
273362

274-
# https://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html
275363
xlib.XGetWindowProperty(
276364
display,
277365
window_xid,
@@ -451,6 +539,9 @@ def send_mouse_click(self, x: int, y: int, button: ButtonCodes = ButtonCodes.But
451539

452540

453541
def get_active_window_xid() -> int:
542+
"""
543+
Returns the XID of the active window.
544+
"""
454545
return get_window_property(
455546
root_window,
456547
"_NET_ACTIVE_WINDOW",
@@ -459,6 +550,10 @@ def get_active_window_xid() -> int:
459550

460551

461552
def get_connected_xids(window):
553+
"""
554+
https://tronche.com/gui/x/xlib/window-information/XQueryTree.html\n
555+
Uses XQueryTree to get the XIDs of connected windows.
556+
"""
462557
root_return = c_ulong()
463558
parent_return = c_ulong()
464559
children_return = POINTER(c_ulong)()
@@ -478,10 +573,16 @@ def get_connected_xids(window):
478573
for xid in range(nitems_return.value):
479574
xids.append(children_return[xid])
480575

576+
# don't forget to free the memory or you will be fucked
577+
xlib.XFree(children_return)
578+
481579
return xids
482580

483581

484582
def get_all_windows() -> list:
583+
"""
584+
Get all window XIDs. By recursively getting all connected windows.
585+
"""
485586
final = get_connected_xids(root_window)
486587
next = final.copy()
487588

0 commit comments

Comments
 (0)