Merge ~pieq/checkbox-support:remove-xrandr-contrib into checkbox-support:master
- Git
- lp:~pieq/checkbox-support
- remove-xrandr-contrib
- Merge into master
Proposed by
Pierre Equoy
Status: | Merged |
---|---|
Approved by: | Sylvain Pineau |
Approved revision: | 544e95491f3752cea8fd600d8c28e29427b23dca |
Merged at revision: | 682b5fa8c4ee4c885ecb5585c06d731cd308a472 |
Proposed branch: | ~pieq/checkbox-support:remove-xrandr-contrib |
Merge into: | checkbox-support:master |
Diff against target: |
1075 lines (+0/-1064) 1 file modified
dev/null (+0/-1064) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sylvain Pineau (community) | Approve | ||
Review via email: mp+424147@code.launchpad.net |
Commit message
Description of the change
Following the changes made for lp:1968943, this commit removes an unused package from checkbox_
Please check https:/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/checkbox_support/contrib/__init__.py b/checkbox_support/contrib/__init__.py | |||
2 | 0 | deleted file mode 100644 | 0 | deleted file mode 100644 |
3 | index e69de29..0000000 | |||
4 | --- a/checkbox_support/contrib/__init__.py | |||
5 | +++ /dev/null | |||
6 | diff --git a/checkbox_support/contrib/xrandr.py b/checkbox_support/contrib/xrandr.py | |||
7 | 1 | deleted file mode 100644 | 1 | deleted file mode 100644 |
8 | index 91fd173..0000000 | |||
9 | --- a/checkbox_support/contrib/xrandr.py | |||
10 | +++ /dev/null | |||
11 | @@ -1,1064 +0,0 @@ | |||
12 | 1 | # Python-XRandR provides a high level API for the XRandR extension of the | ||
13 | 2 | # X.org server. XRandR allows to configure resolution, refresh rate, rotation | ||
14 | 3 | # of the screen and multiple outputs of graphics cards. | ||
15 | 4 | # | ||
16 | 5 | # Copyright 2014 © Canonical Ltd. | ||
17 | 6 | # Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de> | ||
18 | 7 | # Copyright 2007 © Michael Vogt <mvo@ubuntu.com> | ||
19 | 8 | # Copyright 2007 © Canonical Ltd. | ||
20 | 9 | # | ||
21 | 10 | # In many aspects it follows the design of the xrand tool of the X.org, which | ||
22 | 11 | # comes with the following copyright: | ||
23 | 12 | # | ||
24 | 13 | # Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc. | ||
25 | 14 | # Copyright © 2002 Hewlett Packard Company, Inc. | ||
26 | 15 | # Copyright © 2006 Intel Corporation | ||
27 | 16 | # | ||
28 | 17 | # And can be downloaded here: | ||
29 | 18 | # | ||
30 | 19 | # git://anongit.freedesktop.org/git/xorg/app/xrandr | ||
31 | 20 | # | ||
32 | 21 | # This library is free software; you can redistribute it and/or | ||
33 | 22 | # modify it under the terms of the GNU Lesser General Public | ||
34 | 23 | # License as published by the Free Software Foundation; either | ||
35 | 24 | # version 2.1 of the License, or any later version. | ||
36 | 25 | # | ||
37 | 26 | # This library is distributed in the hope that it will be useful, | ||
38 | 27 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
39 | 28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
40 | 29 | # Lesser General Public License for more details. | ||
41 | 30 | # | ||
42 | 31 | # You should have received a copy of the GNU Lesser General Public | ||
43 | 32 | # License along with this library; if not, write to the Free Software | ||
44 | 33 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
45 | 34 | # MA 02110-1301 USA | ||
46 | 35 | |||
47 | 36 | from ctypes import ( | ||
48 | 37 | POINTER, | ||
49 | 38 | Structure, | ||
50 | 39 | byref, | ||
51 | 40 | c_char_p, | ||
52 | 41 | c_void_p, | ||
53 | 42 | c_int, | ||
54 | 43 | c_long, | ||
55 | 44 | c_ulong, | ||
56 | 45 | c_ushort, | ||
57 | 46 | cdll, | ||
58 | 47 | ) | ||
59 | 48 | import os | ||
60 | 49 | |||
61 | 50 | RR_ROTATE_0 = 1 | ||
62 | 51 | RR_ROTATE_90 = 2 | ||
63 | 52 | RR_ROTATE_180 = 4 | ||
64 | 53 | RR_ROTATE_270 = 8 | ||
65 | 54 | RR_REFLECT_X = 16 | ||
66 | 55 | RR_REFLECT_Y = 32 | ||
67 | 56 | |||
68 | 57 | RR_CONNECTED = 0 | ||
69 | 58 | RR_DISCONNECTED = 1 | ||
70 | 59 | RR_UNKOWN_CONNECTION = 2 | ||
71 | 60 | |||
72 | 61 | RR_BAD_OUTPUT = 0 | ||
73 | 62 | RR_BAD_CRTC = 1 | ||
74 | 63 | RR_BAD_MODE = 2 | ||
75 | 64 | |||
76 | 65 | RR_SET_CONFIG_SUCCESS = 0 | ||
77 | 66 | RR_SET_CONFIG_INVALID_CONFIG_TIME = 1 | ||
78 | 67 | RR_SET_CONFIG_INVALID_TIME = 2 | ||
79 | 68 | RR_SET_CONFIG_FAILED = 3 | ||
80 | 69 | |||
81 | 70 | # Flags to keep track of changes | ||
82 | 71 | CHANGES_NONE = 0 | ||
83 | 72 | CHANGES_CRTC = 1 | ||
84 | 73 | CHANGES_MODE = 2 | ||
85 | 74 | CHANGES_RELATION = 4 | ||
86 | 75 | CHANGES_POSITION = 8 | ||
87 | 76 | CHANGES_ROTATION = 16 | ||
88 | 77 | CHANGES_REFLECTION = 32 | ||
89 | 78 | CHANGES_AUTOMATIC = 64 | ||
90 | 79 | CHANGES_REFRESH = 128 | ||
91 | 80 | CHANGES_PROPERTY = 256 | ||
92 | 81 | |||
93 | 82 | # Relation information | ||
94 | 83 | RELATION_ABOVE = 0 | ||
95 | 84 | RELATION_BELOW = 1 | ||
96 | 85 | RELATION_RIGHT_OF = 2 | ||
97 | 86 | RELATION_LEFT_OF = 3 | ||
98 | 87 | RELATION_SAME_AS = 4 | ||
99 | 88 | |||
100 | 89 | # some fundamental datatypes | ||
101 | 90 | RRCrtc = c_long | ||
102 | 91 | RROutput = c_long | ||
103 | 92 | RRMode = c_long | ||
104 | 93 | Connection = c_ushort | ||
105 | 94 | SubpixelOrder = c_ushort | ||
106 | 95 | Time = c_ulong | ||
107 | 96 | Rotation = c_ushort | ||
108 | 97 | Status = c_int | ||
109 | 98 | |||
110 | 99 | # load the libs | ||
111 | 100 | xlib = cdll.LoadLibrary('libX11.so.6') | ||
112 | 101 | rr = cdll.LoadLibrary('libXrandr.so.2') | ||
113 | 102 | |||
114 | 103 | |||
115 | 104 | # query resources | ||
116 | 105 | class _XRRModeInfo(Structure): | ||
117 | 106 | _fields_ = [ | ||
118 | 107 | ("id", RRMode), # XID is c_long | ||
119 | 108 | ("width", c_int), | ||
120 | 109 | ("height", c_int), | ||
121 | 110 | ("dotClock", c_long), | ||
122 | 111 | ("hSyncStart", c_int), | ||
123 | 112 | ("hSyncEnd", c_int), | ||
124 | 113 | ("hTotal", c_int), | ||
125 | 114 | ("hSkew", c_int), | ||
126 | 115 | ("vSyncStart", c_int), | ||
127 | 116 | ("vSyncEnd", c_int), | ||
128 | 117 | ("vTotal", c_int), | ||
129 | 118 | ("name", c_char_p), | ||
130 | 119 | ("nameLength", c_int), | ||
131 | 120 | ("modeFlags", c_long), | ||
132 | 121 | ] | ||
133 | 122 | |||
134 | 123 | |||
135 | 124 | class _XRRScreenSize(Structure): | ||
136 | 125 | _fields_ = [ | ||
137 | 126 | ("width", c_int), | ||
138 | 127 | ("height", c_int), | ||
139 | 128 | ("mwidth", c_int), | ||
140 | 129 | ("mheight", c_int) | ||
141 | 130 | ] | ||
142 | 131 | |||
143 | 132 | |||
144 | 133 | class _XRRCrtcInfo(Structure): | ||
145 | 134 | _fields_ = [ | ||
146 | 135 | ("timestamp", Time), | ||
147 | 136 | ("x", c_int), | ||
148 | 137 | ("y", c_int), | ||
149 | 138 | ("width", c_int), | ||
150 | 139 | ("height", c_int), | ||
151 | 140 | ("mode", RRMode), | ||
152 | 141 | ("rotation", c_int), | ||
153 | 142 | ("noutput", c_int), | ||
154 | 143 | ("outputs", POINTER(RROutput)), | ||
155 | 144 | ("rotations", Rotation), | ||
156 | 145 | ("npossible", c_int), | ||
157 | 146 | ("possible", POINTER(RROutput)), | ||
158 | 147 | ] | ||
159 | 148 | |||
160 | 149 | |||
161 | 150 | class _XRRScreenResources(Structure): | ||
162 | 151 | _fields_ = [ | ||
163 | 152 | ("timestamp", Time), | ||
164 | 153 | ("configTimestamp", Time), | ||
165 | 154 | ("ncrtc", c_int), | ||
166 | 155 | ("crtcs", POINTER(RRCrtc)), | ||
167 | 156 | ("noutput", c_int), | ||
168 | 157 | ("outputs", POINTER(RROutput)), | ||
169 | 158 | ("nmode", c_int), | ||
170 | 159 | ("modes", POINTER(_XRRModeInfo)), | ||
171 | 160 | ] | ||
172 | 161 | |||
173 | 162 | |||
174 | 163 | class RRError(Exception): | ||
175 | 164 | """Base exception class of the module""" | ||
176 | 165 | pass | ||
177 | 166 | |||
178 | 167 | |||
179 | 168 | class UnsupportedRRError(RRError): | ||
180 | 169 | """Raised if the required XRandR extension version is not available""" | ||
181 | 170 | def __init__(self, required, current): | ||
182 | 171 | self.required = required | ||
183 | 172 | self.current = current | ||
184 | 173 | |||
185 | 174 | |||
186 | 175 | # XRRGetOutputInfo | ||
187 | 176 | class _XRROutputInfo(Structure): | ||
188 | 177 | _fields_ = [ | ||
189 | 178 | ("timestamp", Time), | ||
190 | 179 | ("crtc", c_int), | ||
191 | 180 | ("name", c_char_p), | ||
192 | 181 | ("nameLen", c_int), | ||
193 | 182 | ("mm_width", c_ulong), | ||
194 | 183 | ("mm_height", c_ulong), | ||
195 | 184 | ("connection", Connection), | ||
196 | 185 | ("subpixel_order", SubpixelOrder), | ||
197 | 186 | ("ncrtc", c_int), | ||
198 | 187 | ("crtcs", POINTER(RRCrtc)), | ||
199 | 188 | ("nclone", c_int), | ||
200 | 189 | ("clones", POINTER(RROutput)), | ||
201 | 190 | ("nmode", c_int), | ||
202 | 191 | ("npreferred", c_int), | ||
203 | 192 | ("modes", POINTER(RRMode)) | ||
204 | 193 | ] | ||
205 | 194 | |||
206 | 195 | |||
207 | 196 | class _XRRCrtcGamma(Structure): | ||
208 | 197 | _fields_ = [ | ||
209 | 198 | ('size', c_int), | ||
210 | 199 | ('red', POINTER(c_ushort)), | ||
211 | 200 | ('green', POINTER(c_ushort)), | ||
212 | 201 | ('blue', POINTER(c_ushort)), | ||
213 | 202 | ] | ||
214 | 203 | |||
215 | 204 | |||
216 | 205 | def _array_conv(array, type, conv=lambda x: x): | ||
217 | 206 | length = len(array) | ||
218 | 207 | res = (type * length)() | ||
219 | 208 | for i in range(length): | ||
220 | 209 | res[i] = conv(array[i]) | ||
221 | 210 | return res | ||
222 | 211 | |||
223 | 212 | |||
224 | 213 | class Output(object): | ||
225 | 214 | """The output is a reference to a supported output jacket of the graphics | ||
226 | 215 | card. Outputs are attached to a hardware pipe to be used. Furthermore | ||
227 | 216 | they can be a clone of another output or show a subset of the screen""" | ||
228 | 217 | def __init__(self, info, id, screen): | ||
229 | 218 | """Initializes an output instance""" | ||
230 | 219 | self._info = info | ||
231 | 220 | self.id = id | ||
232 | 221 | self._screen = screen | ||
233 | 222 | # Store changes later here | ||
234 | 223 | self._mode = None | ||
235 | 224 | self._crtc = None | ||
236 | 225 | self._rotation = RR_ROTATE_0 | ||
237 | 226 | self._relation = None | ||
238 | 227 | self._relation_offset = 0 | ||
239 | 228 | self._relative_to = None | ||
240 | 229 | self._position = None | ||
241 | 230 | self._reflection = None | ||
242 | 231 | self._automatic = None | ||
243 | 232 | self._rate = None | ||
244 | 233 | self._changes = CHANGES_NONE | ||
245 | 234 | self._x = 0 | ||
246 | 235 | self._y = 0 | ||
247 | 236 | |||
248 | 237 | self.name = self._info.contents.name | ||
249 | 238 | |||
250 | 239 | def __del__(self): | ||
251 | 240 | """Frees the internal reference to the output info if the output gets | ||
252 | 241 | removed""" | ||
253 | 242 | rr.XRRFreeOutputInfo(self._info) | ||
254 | 243 | |||
255 | 244 | def get_physical_width(self): | ||
256 | 245 | """Returns the display width reported by the connected output device""" | ||
257 | 246 | return self._info.contents.mm_width | ||
258 | 247 | |||
259 | 248 | def get_physical_height(self): | ||
260 | 249 | """ | ||
261 | 250 | Returns the display height reported by the connected output device | ||
262 | 251 | """ | ||
263 | 252 | return self._info.contents.mm_height | ||
264 | 253 | |||
265 | 254 | def get_crtc(self): | ||
266 | 255 | """Returns the xid of the hardware pipe to which the output is | ||
267 | 256 | attached. If the output is disabled it will return 0""" | ||
268 | 257 | return self._info.contents.crtc | ||
269 | 258 | |||
270 | 259 | def get_crtcs(self): | ||
271 | 260 | """Returns the xids of the hardware pipes to which the output could | ||
272 | 261 | be attached""" | ||
273 | 262 | crtcs = [] | ||
274 | 263 | for i in range(self._info.contents.ncrtc): | ||
275 | 264 | for crtc in self._screen.crtcs: | ||
276 | 265 | if crtc.xid == self._info.contents.crtcs[i]: | ||
277 | 266 | crtcs.append(crtc) | ||
278 | 267 | return crtcs | ||
279 | 268 | |||
280 | 269 | def get_available_rotations(self): | ||
281 | 270 | """Returns a binary flag of the supported rotations of the output or | ||
282 | 271 | 0 if the output is disabled""" | ||
283 | 272 | rotations = RR_ROTATE_0 | ||
284 | 273 | found = False | ||
285 | 274 | if self.is_active(): | ||
286 | 275 | # Get the rotations supported by all crtcs to make assigning | ||
287 | 276 | # crtcs easier. Furthermore there don't seem to be so many | ||
288 | 277 | # cards which show another behavior | ||
289 | 278 | for crtc in self.get_crtcs(): | ||
290 | 279 | # Set rotations to the value of the first found crtc and | ||
291 | 280 | # then create a subset only for all other crtcs | ||
292 | 281 | if not found: | ||
293 | 282 | rotations = crtc.get_available_rotations() | ||
294 | 283 | found = True | ||
295 | 284 | else: | ||
296 | 285 | rotations = rotations & crtc.get_available_rotations() | ||
297 | 286 | return rotations | ||
298 | 287 | |||
299 | 288 | def get_available_modes(self): | ||
300 | 289 | """Returns the list of supported mode lines (resolution, refresh rate) | ||
301 | 290 | that are supported by the connected device""" | ||
302 | 291 | modes = [] | ||
303 | 292 | for m in range(self._info.contents.nmode): | ||
304 | 293 | output_modes = self._info.contents.modes | ||
305 | 294 | for s in range(self._screen._resources.contents.nmode): | ||
306 | 295 | screen_modes = self._screen._resources.contents.modes | ||
307 | 296 | if screen_modes[s].id == output_modes[m]: | ||
308 | 297 | modes.append(screen_modes[s]) | ||
309 | 298 | return modes | ||
310 | 299 | |||
311 | 300 | def get_preferred_mode(self): | ||
312 | 301 | """Returns an index that refers to the list of available modes and | ||
313 | 302 | points to the preferred mode of the connected device""" | ||
314 | 303 | return self._info.contents.npreferred | ||
315 | 304 | |||
316 | 305 | def is_active(self): | ||
317 | 306 | """Returns True if the output is attached to a hardware pipe, is | ||
318 | 307 | enabled""" | ||
319 | 308 | return self._info.contents.crtc != 0 | ||
320 | 309 | |||
321 | 310 | def is_connected(self): | ||
322 | 311 | """Return True if a device is detected at the output""" | ||
323 | 312 | if self._info.contents.connection in (RR_CONNECTED, | ||
324 | 313 | RR_UNKOWN_CONNECTION): | ||
325 | 314 | return True | ||
326 | 315 | return False | ||
327 | 316 | |||
328 | 317 | def disable(self): | ||
329 | 318 | """Disables the output""" | ||
330 | 319 | if not self.is_active(): | ||
331 | 320 | return | ||
332 | 321 | self._mode = None | ||
333 | 322 | self._crtc._outputs.remove(self) | ||
334 | 323 | self._crtc = None | ||
335 | 324 | self._changes = self._changes | CHANGES_CRTC | CHANGES_MODE | ||
336 | 325 | |||
337 | 326 | def set_to_mode(self, mode): | ||
338 | 327 | modes = self.get_available_modes() | ||
339 | 328 | if mode in range(len(modes)): | ||
340 | 329 | self._mode = modes[mode].id | ||
341 | 330 | return | ||
342 | 331 | raise RRError("Mode is not available") | ||
343 | 332 | |||
344 | 333 | def set_to_preferred_mode(self): | ||
345 | 334 | modes = self.get_available_modes() | ||
346 | 335 | mode = modes[self.get_preferred_mode()] | ||
347 | 336 | if mode is not None: | ||
348 | 337 | self._mode = mode.id | ||
349 | 338 | return | ||
350 | 339 | raise RRError("Preferred mode is not available") | ||
351 | 340 | |||
352 | 341 | def get_clones(self): | ||
353 | 342 | """Returns the xids of the outputs which can be clones of the output""" | ||
354 | 343 | clones = [] | ||
355 | 344 | for i in range(self._info.contents.nclone): | ||
356 | 345 | id = self._info.contents.clones[i] | ||
357 | 346 | o = self._screen.get_output_by_id(id) | ||
358 | 347 | clones.append(o) | ||
359 | 348 | return clones | ||
360 | 349 | |||
361 | 350 | def set_relation(self, relative, relation, offset=0): | ||
362 | 351 | """Sets the position of the output in relation to the given one""" | ||
363 | 352 | rel = self._screen.get_output_by_name(relative) | ||
364 | 353 | if rel and relation in (RELATION_LEFT_OF, RELATION_RIGHT_OF, | ||
365 | 354 | RELATION_ABOVE, RELATION_BELOW, | ||
366 | 355 | RELATION_SAME_AS): | ||
367 | 356 | self._relation = relation | ||
368 | 357 | self._relative_to = rel | ||
369 | 358 | self._relation_offset = offset | ||
370 | 359 | self._changes = self._changes | CHANGES_RELATION | ||
371 | 360 | else: | ||
372 | 361 | raise RRError("The given relative output or relation is not " | ||
373 | 362 | "available") | ||
374 | 363 | |||
375 | 364 | def has_changed(self, changes=None): | ||
376 | 365 | """Checks if the output has changed: Either for a specific change or | ||
377 | 366 | generally""" | ||
378 | 367 | if changes: | ||
379 | 368 | return self._changes & changes | ||
380 | 369 | else: | ||
381 | 370 | return self._changes != CHANGES_NONE | ||
382 | 371 | |||
383 | 372 | |||
384 | 373 | class Crtc(object): | ||
385 | 374 | """The crtc is a reference to a hardware pipe that is provided by the | ||
386 | 375 | graphics device. Outputs can be attached to crtcs""" | ||
387 | 376 | |||
388 | 377 | def __init__(self, info, xid, screen): | ||
389 | 378 | """Initializes the hardware pipe object""" | ||
390 | 379 | self._info = info | ||
391 | 380 | self.xid = xid | ||
392 | 381 | self._screen = screen | ||
393 | 382 | self._outputs = [] | ||
394 | 383 | |||
395 | 384 | def __del__(self): | ||
396 | 385 | """Frees the reference to the rendering pipe if the instance gets | ||
397 | 386 | removed""" | ||
398 | 387 | rr.XRRFreeCrtcInfo(self._info) | ||
399 | 388 | |||
400 | 389 | def get_xid(self): | ||
401 | 390 | """Returns the internal id of the crtc from the X server""" | ||
402 | 391 | return self.xid | ||
403 | 392 | |||
404 | 393 | def get_available_rotations(self): | ||
405 | 394 | """Returns a binary flag that contains the supported rotations of the | ||
406 | 395 | hardware pipe""" | ||
407 | 396 | return self._info.contents.rotations | ||
408 | 397 | |||
409 | 398 | def set_config(self, x, y, mode, outputs, rotation=RR_ROTATE_0): | ||
410 | 399 | """Configures the render pipe with the given mode and outputs. X and y | ||
411 | 400 | set the position of the crtc output in the screen""" | ||
412 | 401 | rr.XRRSetCrtcConfig(self._screen._display, | ||
413 | 402 | self._screen._resources, | ||
414 | 403 | self.xid, | ||
415 | 404 | self._screen.get_timestamp(), | ||
416 | 405 | c_int(x), c_int(y), | ||
417 | 406 | mode, | ||
418 | 407 | rotation, | ||
419 | 408 | _array_conv(outputs, RROutput, lambda x: x.id), | ||
420 | 409 | len(outputs)) | ||
421 | 410 | |||
422 | 411 | def apply_changes(self): | ||
423 | 412 | """Applies the stored changes""" | ||
424 | 413 | if len(self._outputs) > 0: | ||
425 | 414 | output = self._outputs[0] | ||
426 | 415 | self.set_config(output._x, output._y, output._mode, | ||
427 | 416 | self._outputs, output._rotation) | ||
428 | 417 | else: | ||
429 | 418 | self.disable() | ||
430 | 419 | |||
431 | 420 | def disable(self): | ||
432 | 421 | """Turns off all outputs on the crtc""" | ||
433 | 422 | rr.XRRSetCrtcConfig(self._screen._display, | ||
434 | 423 | self._screen._resources, | ||
435 | 424 | self.xid, | ||
436 | 425 | self._screen.get_timestamp(), | ||
437 | 426 | 0, 0, | ||
438 | 427 | None, | ||
439 | 428 | RR_ROTATE_0, | ||
440 | 429 | 0, 0) | ||
441 | 430 | |||
442 | 431 | #FIXME: support gamma settings | ||
443 | 432 | """ | ||
444 | 433 | def get_gamma_size(self): | ||
445 | 434 | return rr.XRRGetCrtcGammaSize(self._screen._display, self.id) | ||
446 | 435 | def get_gamma(self): | ||
447 | 436 | result = rr.XRRGetCrtcGamma(self._screen._display, self.id) | ||
448 | 437 | return _from_gamma(result) | ||
449 | 438 | def set_gamma(self, gamma): | ||
450 | 439 | g = _to_gamma(gamma) | ||
451 | 440 | rr.XRRSetCrtcGamma(self._screen._display, self.id, g) | ||
452 | 441 | rr.XRRFreeGamma(g) | ||
453 | 442 | gamma = property(get_gamma, set_gamma)""" | ||
454 | 443 | |||
455 | 444 | def load_outputs(self): | ||
456 | 445 | """Get the currently assigned outputs""" | ||
457 | 446 | outputs = [] | ||
458 | 447 | for i in range(self._info.contents.noutput): | ||
459 | 448 | id = self._info.contents.outputs[i] | ||
460 | 449 | o = self._screen.get_output_by_id(id) | ||
461 | 450 | outputs.append(o) | ||
462 | 451 | self._outputs = outputs | ||
463 | 452 | |||
464 | 453 | def get_outputs(self): | ||
465 | 454 | """Returns the list of attached outputs""" | ||
466 | 455 | return self._outputs | ||
467 | 456 | |||
468 | 457 | def add_output(self, output): | ||
469 | 458 | """Adds the specified output to the crtc""" | ||
470 | 459 | output._crtc = self | ||
471 | 460 | self._outputs.append(output) | ||
472 | 461 | |||
473 | 462 | def supports_output(self, output): | ||
474 | 463 | """Check if the output can be used by the crtc. | ||
475 | 464 | See check_crtc_for_output in xrandr.c""" | ||
476 | 465 | if not self.xid in [c.xid for c in output.get_crtcs()]: | ||
477 | 466 | return False | ||
478 | 467 | if len(self._outputs): | ||
479 | 468 | for other in self._outputs: | ||
480 | 469 | if other == output: | ||
481 | 470 | continue | ||
482 | 471 | if other._x != output._x: | ||
483 | 472 | return False | ||
484 | 473 | if other._y != output._y: | ||
485 | 474 | return False | ||
486 | 475 | if other._mode != output._mode: | ||
487 | 476 | return False | ||
488 | 477 | if other._rotation != output._rotation: | ||
489 | 478 | return False | ||
490 | 479 | #FIXME: pick_crtc is still missing | ||
491 | 480 | elif self._info.contents.noutput > 0: | ||
492 | 481 | if self._info.contents.x != output._x: | ||
493 | 482 | return False | ||
494 | 483 | if self._info.contents.y != output._y: | ||
495 | 484 | return False | ||
496 | 485 | if self._info.contents.mode_info != output._mode: | ||
497 | 486 | return False | ||
498 | 487 | if self._info.rotation != output._rotation: | ||
499 | 488 | return False | ||
500 | 489 | return True | ||
501 | 490 | |||
502 | 491 | def supports_rotation(self, rotation): | ||
503 | 492 | """Check if the given rotation is supported by the crtc""" | ||
504 | 493 | rotations = self._info.contents.rotations | ||
505 | 494 | dir = rotation & (RR_ROTATE_0 | RR_ROTATE_90 | RR_ROTATE_180 | | ||
506 | 495 | RR_ROTATE_270) | ||
507 | 496 | reflect = rotation & (RR_REFLECT_X | RR_REFLECT_Y) | ||
508 | 497 | if (((rotations & dir) != 0) and ((rotations & reflect) == reflect)): | ||
509 | 498 | return True | ||
510 | 499 | return False | ||
511 | 500 | |||
512 | 501 | def has_changed(self): | ||
513 | 502 | """Check if there are any new outputs assigned to the crtc or any | ||
514 | 503 | outputs with a changed mode or position""" | ||
515 | 504 | if len(self._outputs) != self._info.contents.noutput: | ||
516 | 505 | return True | ||
517 | 506 | for i in range(self._info.contents.noutput): | ||
518 | 507 | id = self._info.contents.outputs[i] | ||
519 | 508 | output = self._screen.get_output_by_id(id) | ||
520 | 509 | if not output in self._outputs: | ||
521 | 510 | return True | ||
522 | 511 | if output.has_changed(): | ||
523 | 512 | return True | ||
524 | 513 | return False | ||
525 | 514 | |||
526 | 515 | |||
527 | 516 | class Screen(object): | ||
528 | 517 | def __init__(self, dpy, screen=-1): | ||
529 | 518 | """Initializes the screen""" | ||
530 | 519 | # Some sane default values | ||
531 | 520 | self.outputs = {} | ||
532 | 521 | self.crtcs = [] | ||
533 | 522 | self._width = 0 | ||
534 | 523 | self._height = 0 | ||
535 | 524 | self._width_max = 0 | ||
536 | 525 | self._height_max = 0 | ||
537 | 526 | self._width_min = 0 | ||
538 | 527 | self._height_min = 0 | ||
539 | 528 | self._width_mm = 0 | ||
540 | 529 | self._height_mm = 0 | ||
541 | 530 | |||
542 | 531 | self._display = dpy | ||
543 | 532 | if not -1 <= screen < xlib.XScreenCount(dpy): | ||
544 | 533 | raise RRError("The chosen screen is not available", screen) | ||
545 | 534 | elif screen == -1: | ||
546 | 535 | self._screen = xlib.XDefaultScreen(dpy) | ||
547 | 536 | else: | ||
548 | 537 | self._screen = screen | ||
549 | 538 | self._root = xlib.XDefaultRootWindow(self._display, self._screen) | ||
550 | 539 | self._id = rr.XRRRootToScreen(self._display, self._root) | ||
551 | 540 | |||
552 | 541 | self._load_resources() | ||
553 | 542 | self._load_config() | ||
554 | 543 | (self._width, self._height, | ||
555 | 544 | self._width_mm, self._height_mm) = self.get_size() | ||
556 | 545 | if XRANDR_VERSION >= (1, 2): | ||
557 | 546 | self._load_screen_size_range() | ||
558 | 547 | self._load_crtcs() | ||
559 | 548 | self._load_outputs() | ||
560 | 549 | |||
561 | 550 | # Store XRandR 1.0 changes here | ||
562 | 551 | self._rate = self.get_current_rate() | ||
563 | 552 | self._rotation = self.get_current_rotation() | ||
564 | 553 | self._size_index = self.get_current_size_index() | ||
565 | 554 | |||
566 | 555 | def __del__(self): | ||
567 | 556 | """Free the reference to the interal screen config if the screen | ||
568 | 557 | gets removed""" | ||
569 | 558 | rr.XRRFreeScreenConfigInfo(self._config) | ||
570 | 559 | |||
571 | 560 | def _load_config(self): | ||
572 | 561 | """Loads the screen configuration. Only needed privately by the | ||
573 | 562 | the bindings""" | ||
574 | 563 | class XRRScreenConfiguration(Structure): | ||
575 | 564 | " private to Xrandr " | ||
576 | 565 | pass | ||
577 | 566 | gsi = rr.XRRGetScreenInfo | ||
578 | 567 | gsi.restype = POINTER(XRRScreenConfiguration) | ||
579 | 568 | self._config = gsi(self._display, self._root) | ||
580 | 569 | |||
581 | 570 | def _load_screen_size_range(self): | ||
582 | 571 | """Detects the dimensionios of the screen""" | ||
583 | 572 | minWidth = c_int() | ||
584 | 573 | minHeight = c_int() | ||
585 | 574 | maxWidth = c_int() | ||
586 | 575 | maxHeight = c_int() | ||
587 | 576 | res = rr.XRRGetScreenSizeRange(self._display, self._root, | ||
588 | 577 | byref(minWidth), byref(minHeight), | ||
589 | 578 | byref(maxWidth), byref(maxHeight)) | ||
590 | 579 | if res: | ||
591 | 580 | self._width_max = maxWidth.value | ||
592 | 581 | self._width_min = minWidth.value | ||
593 | 582 | self._height_max = maxHeight.value | ||
594 | 583 | self._height_min = minHeight.value | ||
595 | 584 | |||
596 | 585 | def _load_resources(self): | ||
597 | 586 | """Loads the screen resources. Only needed privately for the | ||
598 | 587 | bindings""" | ||
599 | 588 | gsr = rr.XRRGetScreenResources | ||
600 | 589 | gsr.restype = POINTER(_XRRScreenResources) | ||
601 | 590 | self._resources = gsr(self._display, self._root) | ||
602 | 591 | |||
603 | 592 | def _load_crtcs(self): | ||
604 | 593 | """Loads the available XRandR 1.2 crtcs (hardware pipes) of | ||
605 | 594 | the screen""" | ||
606 | 595 | gci = rr.XRRGetCrtcInfo | ||
607 | 596 | gci.restype = POINTER(_XRRCrtcInfo) | ||
608 | 597 | c = self._resources.contents.crtcs | ||
609 | 598 | for i in range(self._resources.contents.ncrtc): | ||
610 | 599 | xrrcrtcinfo = gci(self._display, self._resources, c[i]) | ||
611 | 600 | self.crtcs.append(Crtc(xrrcrtcinfo, c[i], self)) | ||
612 | 601 | |||
613 | 602 | def _load_outputs(self): | ||
614 | 603 | """Loads the available XRandR 1.2 outputs of the screen""" | ||
615 | 604 | goi = rr.XRRGetOutputInfo | ||
616 | 605 | goi.restype = POINTER(_XRROutputInfo) | ||
617 | 606 | o = self._resources.contents.outputs | ||
618 | 607 | for i in range(self._resources.contents.noutput): | ||
619 | 608 | xrroutputinfo = goi(self._display, self._resources, o[i]) | ||
620 | 609 | output = Output(xrroutputinfo, o[i], self) | ||
621 | 610 | self.outputs[xrroutputinfo.contents.name] = output | ||
622 | 611 | # Store the mode of the crtc in the output instance | ||
623 | 612 | crtc = self.get_crtc_by_xid(output.get_crtc()) | ||
624 | 613 | if crtc: | ||
625 | 614 | output._mode = crtc._info.contents.mode | ||
626 | 615 | crtc.add_output(output) | ||
627 | 616 | |||
628 | 617 | def get_size(self): | ||
629 | 618 | """Returns the current pixel and physical size of the screen""" | ||
630 | 619 | width = xlib.XDisplayWidth(self._display, self._screen) | ||
631 | 620 | width_mm = xlib.XDisplayWidthMM(self._display, self._screen) | ||
632 | 621 | height = xlib.XDisplayHeight(self._display, self._screen) | ||
633 | 622 | height_mm = xlib.XDisplayHeightMM(self._display, self._screen) | ||
634 | 623 | return width, height, width_mm, height_mm | ||
635 | 624 | |||
636 | 625 | def get_timestamp(self): | ||
637 | 626 | """Creates a X timestamp that must be used when applying changes, since | ||
638 | 627 | they can be delayed""" | ||
639 | 628 | config_timestamp = Time() | ||
640 | 629 | rr.XRRTimes.restpye = c_ulong | ||
641 | 630 | return rr.XRRTimes(self._display, self._id, byref(config_timestamp)) | ||
642 | 631 | |||
643 | 632 | def get_crtc_by_xid(self, xid): | ||
644 | 633 | """Returns the crtc with the given xid or None""" | ||
645 | 634 | for crtc in self.crtcs: | ||
646 | 635 | if crtc.xid == xid: | ||
647 | 636 | return crtc | ||
648 | 637 | return None | ||
649 | 638 | |||
650 | 639 | def get_current_rate(self): | ||
651 | 640 | """Returns the currently used refresh rate""" | ||
652 | 641 | _check_required_version((1, 0)) | ||
653 | 642 | xccr = rr.XRRConfigCurrentRate | ||
654 | 643 | xccr.restype = c_int | ||
655 | 644 | return xccr(self._config) | ||
656 | 645 | |||
657 | 646 | def get_available_rates_for_size_index(self, size_index): | ||
658 | 647 | """Returns the refresh rates that are supported by the screen for | ||
659 | 648 | the given resolution. See get_available_sizes for the resolution to | ||
660 | 649 | which size_index points""" | ||
661 | 650 | _check_required_version((1, 0)) | ||
662 | 651 | rates = [] | ||
663 | 652 | nrates = c_int() | ||
664 | 653 | rr.XRRConfigRates.restype = POINTER(c_ushort) | ||
665 | 654 | _rates = rr.XRRConfigRates(self._config, size_index, byref(nrates)) | ||
666 | 655 | for r in range(nrates.value): | ||
667 | 656 | rates.append(_rates[r]) | ||
668 | 657 | return rates | ||
669 | 658 | |||
670 | 659 | def get_current_rotation(self): | ||
671 | 660 | """Returns the currently used rotation. Can be RR_ROTATE_0, | ||
672 | 661 | RR_ROTATE_90, RR_ROTATE_180 or RR_ROTATE_270""" | ||
673 | 662 | _check_required_version((1, 0)) | ||
674 | 663 | current = c_ushort() | ||
675 | 664 | rr.XRRConfigRotations(self._config, byref(current)) | ||
676 | 665 | return current.value | ||
677 | 666 | |||
678 | 667 | def get_available_rotations(self): | ||
679 | 668 | """Returns a binary flag that holds the available resolutions""" | ||
680 | 669 | _check_required_version((1, 0)) | ||
681 | 670 | current = c_ushort() | ||
682 | 671 | rotations = rr.XRRConfigRotations(self._config, byref(current)) | ||
683 | 672 | return rotations | ||
684 | 673 | |||
685 | 674 | def get_current_size_index(self): | ||
686 | 675 | """Returns the position of the currently used resolution size in the | ||
687 | 676 | list of available resolutions. See get_available_sizes""" | ||
688 | 677 | _check_required_version((1, 0)) | ||
689 | 678 | rotation = c_ushort() | ||
690 | 679 | size = rr.XRRConfigCurrentConfiguration(self._config, | ||
691 | 680 | byref(rotation)) | ||
692 | 681 | return size | ||
693 | 682 | |||
694 | 683 | def get_available_sizes(self): | ||
695 | 684 | """Returns the available resolution sizes of the screen. The size | ||
696 | 685 | index points to the corresponding resolution of this list""" | ||
697 | 686 | _check_required_version((1, 0)) | ||
698 | 687 | sizes = [] | ||
699 | 688 | nsizes = c_int() | ||
700 | 689 | xcs = rr.XRRConfigSizes | ||
701 | 690 | xcs.restype = POINTER(_XRRScreenSize) | ||
702 | 691 | _sizes = xcs(self._config, byref(nsizes)) | ||
703 | 692 | for r in range(nsizes.value): | ||
704 | 693 | sizes.append(_sizes[r]) | ||
705 | 694 | return sizes | ||
706 | 695 | |||
707 | 696 | def set_config(self, size_index, rate, rotation): | ||
708 | 697 | """Configures the screen with the given resolution at the given size | ||
709 | 698 | index, rotation and refresh rate. To get in effect call | ||
710 | 699 | Screen.apply_config()""" | ||
711 | 700 | _check_required_version((1, 0)) | ||
712 | 701 | self.set_size_index(size_index) | ||
713 | 702 | self.set_refresh_rate(rate) | ||
714 | 703 | self.set_rotation(rotation) | ||
715 | 704 | |||
716 | 705 | def set_size_index(self, index): | ||
717 | 706 | """Sets the reoslution of the screen. To get in effect call | ||
718 | 707 | Screen.apply_config()""" | ||
719 | 708 | if index in range(len(self.get_available_sizes())): | ||
720 | 709 | self._size_index = index | ||
721 | 710 | else: | ||
722 | 711 | raise RRError("There isn't any size associated " | ||
723 | 712 | "to the index {}".format(index)) | ||
724 | 713 | |||
725 | 714 | def set_rotation(self, rotation): | ||
726 | 715 | """Sets the rotation of the screen. To get in effect call | ||
727 | 716 | Screen.apply_config()""" | ||
728 | 717 | if self.get_available_rotations() & rotation: | ||
729 | 718 | self._rotation = rotation | ||
730 | 719 | else: | ||
731 | 720 | raise RRError("The chosen rotation is not supported") | ||
732 | 721 | |||
733 | 722 | def set_refresh_rate(self, rate): | ||
734 | 723 | """Sets the refresh rate of the screen. To get in effect call | ||
735 | 724 | Screen.apply_config()""" | ||
736 | 725 | if rate in self.get_available_rates_for_size_index(self._size_index): | ||
737 | 726 | self._rate = rate | ||
738 | 727 | else: | ||
739 | 728 | raise RRError( | ||
740 | 729 | "The chosen refresh rate {0} is not supported".format(rate)) | ||
741 | 730 | |||
742 | 731 | def get_mode_by_xid(self, xid): | ||
743 | 732 | """Returns the mode of the given xid""" | ||
744 | 733 | screen_modes = self._resources.contents.modes | ||
745 | 734 | for s in range(self._resources.contents.nmode): | ||
746 | 735 | if screen_modes[s].id == xid: | ||
747 | 736 | return screen_modes[s] | ||
748 | 737 | return None | ||
749 | 738 | |||
750 | 739 | def get_output_by_name(self, name): | ||
751 | 740 | """Returns the output of the screen with the given name or None""" | ||
752 | 741 | if name in self.outputs: | ||
753 | 742 | return self.outputs[name] | ||
754 | 743 | else: | ||
755 | 744 | return None | ||
756 | 745 | |||
757 | 746 | def get_output_by_id(self, id): | ||
758 | 747 | """Returns the output of the screen with the given xid or None""" | ||
759 | 748 | for o in list(self.outputs.values()): | ||
760 | 749 | if o.id == id: | ||
761 | 750 | return o | ||
762 | 751 | return None | ||
763 | 752 | |||
764 | 753 | def print_info(self, verbose=False): | ||
765 | 754 | """Prints some information about the detected screen and its outputs""" | ||
766 | 755 | _check_required_version((1, 0)) | ||
767 | 756 | print(( | ||
768 | 757 | "Screen {0}: minimum {1} x {2}, current {3} x {4}," | ||
769 | 758 | " maximum {5} x {6}" | ||
770 | 759 | ).format(self._screen, self._width_min, self._height_min, self._width, | ||
771 | 760 | self._height, self._width_max, self._height_max)) | ||
772 | 761 | print(" {0}mm x {0}mm".format(self._width_mm, | ||
773 | 762 | self._height_mm)) | ||
774 | 763 | print("Crtcs: {0}".format(len(self.crtcs))) | ||
775 | 764 | if verbose: | ||
776 | 765 | print("Modes ({0}):".format(self._resources.contents.nmode)) | ||
777 | 766 | modes = self._resources.contents.modes | ||
778 | 767 | for i in range(self._resources.contents.nmode): | ||
779 | 768 | print(" {0} - {1}x{2}".format( | ||
780 | 769 | modes[i].name, modes[i].width, modes[i].height)) | ||
781 | 770 | i = 0 | ||
782 | 771 | print("Sizes @ Refresh Rates:") | ||
783 | 772 | for s in self.get_available_sizes(): | ||
784 | 773 | print(" [{0}] {1} x {2} @ {3}".format( | ||
785 | 774 | i, s.width, s.height, | ||
786 | 775 | self.get_available_rates_for_size_index(i))) | ||
787 | 776 | i += 1 | ||
788 | 777 | print("Rotations:") | ||
789 | 778 | rots = self.get_available_rotations() | ||
790 | 779 | if rots & RR_ROTATE_0: | ||
791 | 780 | print("normal") | ||
792 | 781 | if rots & RR_ROTATE_90: | ||
793 | 782 | print("right") | ||
794 | 783 | if rots & RR_ROTATE_180: | ||
795 | 784 | print("inverted") | ||
796 | 785 | if rots & RR_ROTATE_270: | ||
797 | 786 | print("left") | ||
798 | 787 | print("") | ||
799 | 788 | print("Outputs:") | ||
800 | 789 | for o in list(self.outputs.keys()): | ||
801 | 790 | output = self.outputs[o] | ||
802 | 791 | print(" %s" % o) | ||
803 | 792 | if output.is_connected(): | ||
804 | 793 | print("(%smm x %smm)" % (output.get_physical_width(), | ||
805 | 794 | output.get_physical_height())) | ||
806 | 795 | modes = output.get_available_modes() | ||
807 | 796 | print(" Modes:") | ||
808 | 797 | for m in range(len(modes)): | ||
809 | 798 | mode = modes[m] | ||
810 | 799 | refresh = mode.dotClock / (mode.hTotal * mode.vTotal) | ||
811 | 800 | print(" [%s] %s x %s @ %s" % (m, | ||
812 | 801 | mode.width, | ||
813 | 802 | mode.height, | ||
814 | 803 | refresh)) | ||
815 | 804 | if mode.id == output._mode: | ||
816 | 805 | print("*") | ||
817 | 806 | if m == output.get_preferred_mode(): | ||
818 | 807 | print("(preferred)") | ||
819 | 808 | print("") | ||
820 | 809 | print(" Rotations:") | ||
821 | 810 | rots = output.get_available_rotations() | ||
822 | 811 | if rots & RR_ROTATE_0: | ||
823 | 812 | print("normal") | ||
824 | 813 | if rots & RR_ROTATE_90: | ||
825 | 814 | print("right") | ||
826 | 815 | if rots & RR_ROTATE_180: | ||
827 | 816 | print("inverted") | ||
828 | 817 | if rots & RR_ROTATE_270: | ||
829 | 818 | print("left") | ||
830 | 819 | print("") | ||
831 | 820 | else: | ||
832 | 821 | print("(not connected)") | ||
833 | 822 | if verbose: | ||
834 | 823 | print(" Core properties:") | ||
835 | 824 | for (f, t) in output._info.contents._fields_: | ||
836 | 825 | print(" %s: %s" % ( | ||
837 | 826 | f, getattr(output._info.contents, f))) | ||
838 | 827 | |||
839 | 828 | def get_outputs(self): | ||
840 | 829 | """Returns the outputs of the screen""" | ||
841 | 830 | _check_required_version((1, 2)) | ||
842 | 831 | return list(self.outputs.values()) | ||
843 | 832 | |||
844 | 833 | def get_output_names(self): | ||
845 | 834 | _check_required_version((1, 2)) | ||
846 | 835 | return list(self.outputs.keys()) | ||
847 | 836 | |||
848 | 837 | def set_size(self, width, height, width_mm, height_mm): | ||
849 | 838 | """Apply the given pixel and physical size to the screen""" | ||
850 | 839 | _check_required_version((1, 2)) | ||
851 | 840 | # Check if we really need to apply the changes | ||
852 | 841 | if (width, height, width_mm, height_mm) == self.get_size(): | ||
853 | 842 | return | ||
854 | 843 | rr.XRRSetScreenSize(self._display, self._root, | ||
855 | 844 | c_int(width), c_int(height), | ||
856 | 845 | c_int(width_mm), c_int(height_mm)) | ||
857 | 846 | |||
858 | 847 | def apply_output_config(self): | ||
859 | 848 | """Used for instantly applying RandR 1.2 changes""" | ||
860 | 849 | _check_required_version((1, 2)) | ||
861 | 850 | self._arrange_outputs() | ||
862 | 851 | self._calculate_size() | ||
863 | 852 | self.set_size(self._width, self._height, | ||
864 | 853 | self._width_mm, self._height_mm) | ||
865 | 854 | |||
866 | 855 | # Assign all active outputs to crtcs | ||
867 | 856 | for output in list(self.outputs.values()): | ||
868 | 857 | if not output._mode or output._crtc: | ||
869 | 858 | continue | ||
870 | 859 | for crtc in output.get_crtcs(): | ||
871 | 860 | if crtc and crtc.supports_output(output): | ||
872 | 861 | crtc.add_output(output) | ||
873 | 862 | output._changes = output._changes | CHANGES_CRTC | ||
874 | 863 | break | ||
875 | 864 | if not output._crtc: | ||
876 | 865 | #FIXME: Take a look at the pick_crtc code in xrandr.c | ||
877 | 866 | raise RRError("There is no matching crtc for the output") | ||
878 | 867 | |||
879 | 868 | # Apply stored changes of crtcs | ||
880 | 869 | for crtc in self.crtcs: | ||
881 | 870 | if crtc.has_changed(): | ||
882 | 871 | crtc.apply_changes() | ||
883 | 872 | |||
884 | 873 | def apply_config(self): | ||
885 | 874 | """Used for instantly applying RandR 1.0 changes""" | ||
886 | 875 | _check_required_version((1, 0)) | ||
887 | 876 | status = rr.XRRSetScreenConfigAndRate(self._display, | ||
888 | 877 | self._config, | ||
889 | 878 | self._root, | ||
890 | 879 | self._size_index, | ||
891 | 880 | self._rotation, | ||
892 | 881 | self._rate, | ||
893 | 882 | self.get_timestamp()) | ||
894 | 883 | return status | ||
895 | 884 | |||
896 | 885 | def _arrange_outputs(self): | ||
897 | 886 | """Arrange all output positions according to their relative position""" | ||
898 | 887 | for output in self.get_outputs(): | ||
899 | 888 | # Skip not changed and not used outputs | ||
900 | 889 | if not output.has_changed(CHANGES_RELATION) or \ | ||
901 | 890 | output._mode is None: | ||
902 | 891 | continue | ||
903 | 892 | relative = output._relative_to | ||
904 | 893 | mode = self.get_mode_by_xid(output._mode) | ||
905 | 894 | mode_relative = self.get_mode_by_xid(relative._mode) | ||
906 | 895 | if not relative or not relative._mode: | ||
907 | 896 | output._x = 0 | ||
908 | 897 | output._y = 0 | ||
909 | 898 | output._changes = output._changes | CHANGES_POSITION | ||
910 | 899 | if output._relation == RELATION_LEFT_OF: | ||
911 | 900 | output._y = relative._y + output._relation_offset | ||
912 | 901 | output._x = relative._x - get_mode_width( | ||
913 | 902 | mode, output._rotation) | ||
914 | 903 | elif output._relation == RELATION_RIGHT_OF: | ||
915 | 904 | output._y = relative._y + output._relation_offset | ||
916 | 905 | output._x = relative._x + get_mode_width(mode_relative, | ||
917 | 906 | output._rotation) | ||
918 | 907 | elif output._relation == RELATION_ABOVE: | ||
919 | 908 | output._y = relative._y - get_mode_height(mode, | ||
920 | 909 | output._rotation) | ||
921 | 910 | output._x = relative._x + output._relation_offset | ||
922 | 911 | elif output._relation == RELATION_BELOW: | ||
923 | 912 | output._y = relative._y + get_mode_height(mode_relative, | ||
924 | 913 | output._rotation) | ||
925 | 914 | output._x = relative._x + output._relation_offset | ||
926 | 915 | elif output._relation == RELATION_SAME_AS: | ||
927 | 916 | output._y = relative._y + output._relation_offset | ||
928 | 917 | output._x = relative._x + output._relation_offset | ||
929 | 918 | output._changes = output._changes | CHANGES_POSITION | ||
930 | 919 | # Normalize the postions so to the upper left cornor of all outputs | ||
931 | 920 | # is at 0,0 | ||
932 | 921 | min_x = 32768 | ||
933 | 922 | min_y = 32768 | ||
934 | 923 | for output in self.get_outputs(): | ||
935 | 924 | if output._mode is None: | ||
936 | 925 | continue | ||
937 | 926 | if output._x < min_x: | ||
938 | 927 | min_x = output._x | ||
939 | 928 | if output._y < min_y: | ||
940 | 929 | min_y = output._y | ||
941 | 930 | for output in self.get_outputs(): | ||
942 | 931 | if output._mode is None: | ||
943 | 932 | continue | ||
944 | 933 | output._x -= min_x | ||
945 | 934 | output._y -= min_y | ||
946 | 935 | output._changes = output._changes | CHANGES_POSITION | ||
947 | 936 | |||
948 | 937 | def _calculate_size(self): | ||
949 | 938 | """Recalculate the pixel and physical size of the screen so that | ||
950 | 939 | it covers all outputs""" | ||
951 | 940 | width = self._width | ||
952 | 941 | height = self._height | ||
953 | 942 | for output in self.get_outputs(): | ||
954 | 943 | if not output._mode: | ||
955 | 944 | continue | ||
956 | 945 | mode = self.get_mode_by_xid(output._mode) | ||
957 | 946 | x = output._x | ||
958 | 947 | y = output._y | ||
959 | 948 | w = get_mode_width(mode, output._rotation) | ||
960 | 949 | h = get_mode_height(mode, output._rotation) | ||
961 | 950 | if x + w > width: | ||
962 | 951 | width = x + w | ||
963 | 952 | if y + h > height: | ||
964 | 953 | height = y + h | ||
965 | 954 | if width > self._width_max or height > self._height_max: | ||
966 | 955 | raise RRError("The required size is not supported", | ||
967 | 956 | (width, height), (self._width_max, self._width_min)) | ||
968 | 957 | else: | ||
969 | 958 | if height < self._height_min: | ||
970 | 959 | self._fb_height = self._height_min | ||
971 | 960 | else: | ||
972 | 961 | self._height = height | ||
973 | 962 | if width < self._width_min: | ||
974 | 963 | self._width = self._width_min | ||
975 | 964 | else: | ||
976 | 965 | self._width = width | ||
977 | 966 | #FIXME: Physical size is missing | ||
978 | 967 | |||
979 | 968 | |||
980 | 969 | def get_current_display(): | ||
981 | 970 | """Returns the currently used display""" | ||
982 | 971 | display_url = os.getenv("DISPLAY") | ||
983 | 972 | if display_url is None: | ||
984 | 973 | raise SystemExit("DISPLAY environment variable is not defined!") | ||
985 | 974 | open_display = xlib.XOpenDisplay | ||
986 | 975 | # Set .argtypes and .restype, to ensure proper | ||
987 | 976 | # type check and conversion | ||
988 | 977 | open_display.restype = c_void_p | ||
989 | 978 | open_display.argtypes = [c_char_p] | ||
990 | 979 | # XOpenDisplay accepts a char*, but | ||
991 | 980 | # display_url is a unicode string therefore | ||
992 | 981 | # we convert it to a bytes string | ||
993 | 982 | dpy = open_display(display_url.encode('utf-8')) | ||
994 | 983 | return dpy | ||
995 | 984 | |||
996 | 985 | |||
997 | 986 | def get_current_screen(): | ||
998 | 987 | """Returns the currently used screen""" | ||
999 | 988 | screen = Screen(get_current_display()) | ||
1000 | 989 | return screen | ||
1001 | 990 | |||
1002 | 991 | |||
1003 | 992 | def get_screen_of_display(display, count): | ||
1004 | 993 | """Returns the screen of the given display""" | ||
1005 | 994 | dpy = xlib.XOpenDisplay(display) | ||
1006 | 995 | return Screen(dpy, count) | ||
1007 | 996 | |||
1008 | 997 | |||
1009 | 998 | def get_version(): | ||
1010 | 999 | """Returns a tuple containing the major and minor version of the xrandr | ||
1011 | 1000 | extension or None if the extension is not available""" | ||
1012 | 1001 | major = c_int() | ||
1013 | 1002 | minor = c_int() | ||
1014 | 1003 | res = rr.XRRQueryVersion(get_current_display(), | ||
1015 | 1004 | byref(major), byref(minor)) | ||
1016 | 1005 | if res: | ||
1017 | 1006 | return (major.value, minor.value) | ||
1018 | 1007 | return None | ||
1019 | 1008 | |||
1020 | 1009 | |||
1021 | 1010 | def has_extension(): | ||
1022 | 1011 | """Returns True if the xrandr extension is available""" | ||
1023 | 1012 | if XRANDR_VERSION: | ||
1024 | 1013 | return True | ||
1025 | 1014 | return False | ||
1026 | 1015 | |||
1027 | 1016 | |||
1028 | 1017 | def _to_gamma(gamma): | ||
1029 | 1018 | g = rr.XRRAllocGamma(len(gamma[0])) | ||
1030 | 1019 | for i in range(gamma[0]): | ||
1031 | 1020 | g.red[i] = gamma[0][i] | ||
1032 | 1021 | g.green[i] = gamma[1][i] | ||
1033 | 1022 | g.blue[i] = gamma[2][i] | ||
1034 | 1023 | return g | ||
1035 | 1024 | |||
1036 | 1025 | |||
1037 | 1026 | def _from_gamma(g): | ||
1038 | 1027 | gamma = ([], [], []) | ||
1039 | 1028 | for i in range(g.size): | ||
1040 | 1029 | gamma[0].append(g.red[i]) | ||
1041 | 1030 | gamma[1].append(g.green[i]) | ||
1042 | 1031 | gamma[2].append(g.blue[i]) | ||
1043 | 1032 | rr.XRRFreeGamma(g) | ||
1044 | 1033 | |||
1045 | 1034 | |||
1046 | 1035 | def _check_required_version(version): | ||
1047 | 1036 | """Raises an exception if the given or a later version of xrandr is not | ||
1048 | 1037 | available""" | ||
1049 | 1038 | if XRANDR_VERSION is None or XRANDR_VERSION < version: | ||
1050 | 1039 | raise UnsupportedRRError(version, XRANDR_VERSION) | ||
1051 | 1040 | |||
1052 | 1041 | |||
1053 | 1042 | def get_mode_height(mode, rotation): | ||
1054 | 1043 | """Return the height of the given mode taking the rotation into account""" | ||
1055 | 1044 | if rotation & (RR_ROTATE_0 | RR_ROTATE_180): | ||
1056 | 1045 | return mode.height | ||
1057 | 1046 | elif rotation & (RR_ROTATE_90 | RR_ROTATE_270): | ||
1058 | 1047 | return mode.width | ||
1059 | 1048 | else: | ||
1060 | 1049 | return 0 | ||
1061 | 1050 | |||
1062 | 1051 | |||
1063 | 1052 | def get_mode_width(mode, rotation): | ||
1064 | 1053 | """Return the width of the given mode taking the rotation into account""" | ||
1065 | 1054 | if rotation & (RR_ROTATE_0 | RR_ROTATE_180): | ||
1066 | 1055 | return mode.width | ||
1067 | 1056 | elif rotation & (RR_ROTATE_90 | RR_ROTATE_270): | ||
1068 | 1057 | return mode.height | ||
1069 | 1058 | else: | ||
1070 | 1059 | return 0 | ||
1071 | 1060 | |||
1072 | 1061 | |||
1073 | 1062 | XRANDR_VERSION = get_version() | ||
1074 | 1063 | |||
1075 | 1064 | # vim:ts=4:sw=4:et |
+1