-
Notifications
You must be signed in to change notification settings - Fork 3
/
xlib.py
116 lines (87 loc) · 3.16 KB
/
xlib.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
#!/usr/bin/python3
"""
X11 related classes and functions.
"""
# pylint: disable=wildcard-import
# pylint: disable=unused-wildcard-import
import collections
from ctypes import *
class Xlib:
"""Adapter to X11 library."""
def __init__(self):
self.lib = CDLL("libX11.so.6")
self.lib.XOpenDisplay.argtypes = [c_char_p]
self.lib.XOpenDisplay.restype = c_void_p
self.lib.XDisplayString.argtypes = [c_void_p]
self.lib.XDisplayString.restype = c_char_p
self.lib.XCloseDisplay.argtypes = [c_void_p]
self.lib.XFree.argtypes = [c_void_p]
self.lib.XFree.restype = c_int
self.dpy = None
def open_display(self):
"""Adapts: Display *XOpenDisplay(char *display_name);
See: man 3 xopendisplay
"""
self.dpy = self.lib.XOpenDisplay(None)
return self.dpy
def close_display(self):
"""Adapts: int XCloseDisplay(Display *);
See: man 3 xopendisplay
"""
return self.lib.XCloseDisplay(self.dpy)
def free(self, ptr):
"""Adapts XFree."""
return self.lib.XFree(ptr)
def display_string(self):
"""Adapts XDisplayString."""
return self.lib.XDisplayString(self.dpy).decode('ascii')
class XineramaScreenInfo(Structure):
"""struct XineramaScreenInfo
Definition in /usr/include/X11/extensions/Xinerama.h
"""
# pylint: disable=too-few-public-methods
_fields_ = [('screen_number', c_int),
('x_org', c_short),
('y_org', c_short),
('width', c_short),
('height', c_short)] # yapf: disable
ScreenInfo = collections.namedtuple('ScreenInfo', 'number width height x y')
class Xinerama:
"""Adapter to Xinerama X extension."""
def __init__(self, xlib):
self.xlib = xlib
self.lib = CDLL("libXinerama.so.1")
self.lib.XineramaIsActive.argtypes = [c_void_p]
self.lib.XineramaIsActive.restype = c_bool
self.lib.XineramaQueryScreens.argtypes = [c_void_p, POINTER(c_int)]
self.lib.XineramaQueryScreens.restype = POINTER(XineramaScreenInfo)
def is_active(self):
"""Adapts: Bool XineramaIsActive(Display *);
See: man 3 xinerama
"""
assert self.xlib.dpy
return self.lib.XineramaIsActive(self.xlib.dpy)
def query_screens(self):
""" Adapts: XineramaScreenInfo *XineramaQueryScreens(Display*, int*);
See: man 3 xinerama
"""
dpy = self.xlib.dpy
assert dpy
num = c_int(0)
xscreens = self.lib.XineramaQueryScreens(dpy, byref(num))
screens = [
ScreenInfo(s.screen_number, s.width, s.height, s.x_org, s.y_org)
for s in xscreens[:num.value]
]
self.xlib.free(xscreens)
return screens
def query_screens():
"""Return dict of ScreenInfo objects describing available screens."""
xlib = Xlib()
dpy_ptr = xlib.open_display()
if dpy_ptr is None:
return {}
xinerama = Xinerama(xlib)
all_screens = xinerama.query_screens() if xinerama.is_active() else []
xlib.close_display()
return {str(screen.number): screen for screen in all_screens}