2
# -*- coding: utf-8 -*-
4
# Python-XRandR provides a high level API for the XRandR extension of the
5
# X.org server. XRandR allows to configure resolution, refresh rate, rotation
6
# of the screen and multiple outputs of graphics cards.
8
# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de>
9
# Copyright 2007 © Michael Vogt <mvo@ubuntu.com>
10
# Copyright 2007 © Canonical Ltd.
12
# In many aspects it follows the design of the xrand tool of the X.org, which
13
# comes with the following copyright:
15
# Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
16
# Copyright © 2002 Hewlett Packard Company, Inc.
17
# Copyright © 2006 Intel Corporation
19
# And can be downloaded here:
21
# git://anongit.freedesktop.org/git/xorg/app/xrandr
23
# This library is free software; you can redistribute it and/or
24
# modify it under the terms of the GNU Lesser General Public
25
# License as published by the Free Software Foundation; either
26
# version 2.1 of the License, or any later version.
28
# This library is distributed in the hope that it will be useful,
29
# but WITHOUT ANY WARRANTY; without even the implied warranty of
30
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31
# Lesser General Public License for more details.
33
# You should have received a copy of the GNU Lesser General Public
34
# License along with this library; if not, write to the Free Software
35
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
61
RR_UNKOWN_CONNECTION = 2
67
RR_SET_CONFIG_SUCCESS = 0
68
RR_SET_CONFIG_INVALID_CONFIG_TIME = 1
69
RR_SET_CONFIG_INVALID_TIME = 2
70
RR_SET_CONFIG_FAILED = 3
72
# Flags to keep track of changes
79
CHANGES_REFLECTION = 32
80
CHANGES_AUTOMATIC = 64
82
CHANGES_PROPERTY = 256
84
# Relation information
91
# some fundamental datatypes
96
SubpixelOrder = c_ushort
102
xlib = cdll.LoadLibrary('libX11.so.6')
103
rr = cdll.LoadLibrary('libXrandr.so.2')
107
class _XRRModeInfo(Structure):
109
("id", RRMode), # XID is c_long
112
("dotClock", c_long),
113
("hSyncStart", c_int),
117
("vSyncStart", c_int),
121
("nameLength", c_int),
122
("modeFlags", c_long),
126
class _XRRScreenSize(Structure):
135
class _XRRCrtcInfo(Structure):
145
("outputs", POINTER(RROutput)),
146
("rotations", Rotation),
147
("npossible", c_int),
148
("possible", POINTER(RROutput)),
152
class _XRRScreenResources(Structure):
155
("configTimestamp", Time),
157
("crtcs", POINTER(RRCrtc)),
159
("outputs", POINTER(RROutput)),
161
("modes", POINTER(_XRRModeInfo)),
165
class RRError(Exception):
166
"""Base exception class of the module"""
170
class UnsupportedRRError(RRError):
171
"""Raised if the required XRandR extension version is not available"""
172
def __init__(self, required, current):
173
self.required = required
174
self.current = current
178
class _XRROutputInfo(Structure):
184
("mm_width", c_ulong),
185
("mm_height", c_ulong),
186
("connection", Connection),
187
("subpixel_order", SubpixelOrder),
189
("crtcs", POINTER(RRCrtc)),
191
("clones", POINTER(RROutput)),
193
("npreferred", c_int),
194
("modes", POINTER(RRMode))
198
class _XRRCrtcGamma(Structure):
201
('red', POINTER(c_ushort)),
202
('green', POINTER(c_ushort)),
203
('blue', POINTER(c_ushort)),
207
def _array_conv(array, type, conv=lambda x: x):
209
res = (type * length)()
210
for i in range(length):
211
res[i] = conv(array[i])
216
"""The output is a reference to a supported output jacket of the graphics
217
card. Outputs are attached to a hardware pipe to be used. Furthermore
218
they can be a clone of another output or show a subset of the screen"""
219
def __init__(self, info, id, screen):
220
"""Initializes an output instance"""
223
self._screen = screen
224
# Store changes later here
227
self._rotation = RR_ROTATE_0
228
self._relation = None
229
self._relation_offset = 0
230
self._relative_to = None
231
self._position = None
232
self._reflection = None
233
self._automatic = None
235
self._changes = CHANGES_NONE
239
self.name = self._info.contents.name
242
"""Frees the internal reference to the output info if the output gets
244
rr.XRRFreeOutputInfo(self._info)
246
def get_physical_width(self):
247
"""Returns the display width reported by the connected output device"""
248
return self._info.contents.mm_width
250
def get_physical_height(self):
252
Returns the display height reported by the connected output device
254
return self._info.contents.mm_height
257
"""Returns the xid of the hardware pipe to which the output is
258
attached. If the output is disabled it will return 0"""
259
return self._info.contents.crtc
262
"""Returns the xids of the hardware pipes to which the output could
265
for i in range(self._info.contents.ncrtc):
266
for crtc in self._screen.crtcs:
267
if crtc.xid == self._info.contents.crtcs[i]:
271
def get_available_rotations(self):
272
"""Returns a binary flag of the supported rotations of the output or
273
0 if the output is disabled"""
274
rotations = RR_ROTATE_0
277
# Get the rotations supported by all crtcs to make assigning
278
# crtcs easier. Furthermore there don't seem to be so many
279
# cards which show another behavior
280
for crtc in self.get_crtcs():
281
# Set rotations to the value of the first found crtc and
282
# then create a subset only for all other crtcs
284
rotations = crtc.get_available_rotations()
287
rotations = rotations & crtc.get_available_rotations()
290
def get_available_modes(self):
291
"""Returns the list of supported mode lines (resolution, refresh rate)
292
that are supported by the connected device"""
294
for m in range(self._info.contents.nmode):
295
output_modes = self._info.contents.modes
296
for s in range(self._screen._resources.contents.nmode):
297
screen_modes = self._screen._resources.contents.modes
298
if screen_modes[s].id == output_modes[m]:
299
modes.append(screen_modes[s])
302
def get_preferred_mode(self):
303
"""Returns an index that refers to the list of available modes and
304
points to the preferred mode of the connected device"""
305
return self._info.contents.npreferred
308
"""Returns True if the output is attached to a hardware pipe, is
310
return self._info.contents.crtc != 0
312
def is_connected(self):
313
"""Return True if a device is detected at the output"""
314
if self._info.contents.connection in (RR_CONNECTED,
315
RR_UNKOWN_CONNECTION):
320
"""Disables the output"""
321
if not self.is_active():
324
self._crtc._outputs.remove(self)
326
self._changes = self._changes | CHANGES_CRTC | CHANGES_MODE
328
def set_to_mode(self, mode):
329
modes = self.get_available_modes()
330
if mode in range(len(modes)):
331
self._mode = modes[mode].id
333
raise RRError("Mode is not available")
335
def set_to_preferred_mode(self):
336
modes = self.get_available_modes()
337
mode = modes[self.get_preferred_mode()]
341
raise RRError("Preferred mode is not available")
343
def get_clones(self):
344
"""Returns the xids of the outputs which can be clones of the output"""
346
for i in range(self._info.contents.nclone):
347
id = self._info.contents.clones[i]
348
o = self._screen.get_output_by_id(id)
352
def set_relation(self, relative, relation, offset=0):
353
"""Sets the position of the output in relation to the given one"""
354
rel = self._screen.get_output_by_name(relative)
355
if rel and relation in (RELATION_LEFT_OF, RELATION_RIGHT_OF,
356
RELATION_ABOVE, RELATION_BELOW,
358
self._relation = relation
359
self._relative_to = rel
360
self._relation_offset = offset
361
self._changes = self._changes | CHANGES_RELATION
363
raise RRError("The given relative output or relation is not "
366
def has_changed(self, changes=None):
367
"""Checks if the output has changed: Either for a specific change or
370
return self._changes & changes
372
return self._changes != CHANGES_NONE
376
"""The crtc is a reference to a hardware pipe that is provided by the
377
graphics device. Outputs can be attached to crtcs"""
379
def __init__(self, info, xid, screen):
380
"""Initializes the hardware pipe object"""
383
self._screen = screen
387
"""Frees the reference to the rendering pipe if the instance gets
389
rr.XRRFreeCrtcConfigInfo(self._info)
392
"""Returns the internal id of the crtc from the X server"""
395
def get_available_rotations(self):
396
"""Returns a binary flag that contains the supported rotations of the
398
return self._info.contents.rotations
400
def set_config(self, x, y, mode, outputs, rotation=RR_ROTATE_0):
401
"""Configures the render pipe with the given mode and outputs. X and y
402
set the position of the crtc output in the screen"""
403
rr.XRRSetCrtcConfig(self._screen._display,
404
self._screen._resources,
406
self._screen.get_timestamp(),
410
_array_conv(outputs, RROutput, lambda x: x.id),
413
def apply_changes(self):
414
"""Applies the stored changes"""
415
if len(self._outputs) > 0:
416
output = self._outputs[0]
417
self.set_config(output._x, output._y, output._mode,
418
self._outputs, output._rotation)
423
"""Turns off all outputs on the crtc"""
424
rr.XRRSetCrtcConfig(self._screen._display,
425
self._screen._resources,
427
self._screen.get_timestamp(),
433
#FIXME: support gamma settings
435
def get_gamma_size(self):
436
return rr.XRRGetCrtcGammaSize(self._screen._display, self.id)
438
result = rr.XRRGetCrtcGamma(self._screen._display, self.id)
439
return _from_gamma(result)
440
def set_gamma(self, gamma):
442
rr.XRRSetCrtcGamma(self._screen._display, self.id, g)
444
gamma = property(get_gamma, set_gamma)"""
446
def load_outputs(self):
447
"""Get the currently assigned outputs"""
449
for i in range(self._info.contents.noutput):
450
id = self._info.contents.outputs[i]
451
o = self._screen.get_output_by_id(id)
453
self._outputs = outputs
455
def get_outputs(self):
456
"""Returns the list of attached outputs"""
459
def add_output(self, output):
460
"""Adds the specified output to the crtc"""
462
self._outputs.append(output)
464
def supports_output(self, output):
465
"""Check if the output can be used by the crtc.
466
See check_crtc_for_output in xrandr.c"""
467
if not self.xid in [c.xid for c in output.get_crtcs()]:
469
if len(self._outputs):
470
for other in self._outputs:
473
if other._x != output._x:
475
if other._y != output._y:
477
if other._mode != output._mode:
479
if other._rotation != output._rotation:
481
#FIXME: pick_crtc is still missing
482
elif self._info.contents.noutput > 0:
483
if self._info.contents.x != output._x:
485
if self._info.contents.y != output._y:
487
if self._info.contents.mode_info != output._mode:
489
if self._info.rotation != output._rotation:
493
def supports_rotation(self, rotation):
494
"""Check if the given rotation is supported by the crtc"""
495
rotations = self._info.contents.rotations
496
dir = rotation & (RR_ROTATE_0 | RR_ROTATE_90 | RR_ROTATE_180 |
498
reflect = rotation & (RR_REFLECT_X | RR_REFLECT_Y)
499
if (((rotations & dir) != 0) and ((rotations & reflect) == reflect)):
503
def has_changed(self):
504
"""Check if there are any new outputs assigned to the crtc or any
505
outputs with a changed mode or position"""
506
if len(self._outputs) != self._info.contents.noutput:
508
for i in range(self._info.contents.noutput):
509
id = self._info.contents.outputs[i]
510
output = self._screen.get_output_by_id(id)
511
if not output in self._outputs:
513
if output.has_changed():
519
def __init__(self, dpy, screen=-1):
520
"""Initializes the screen"""
521
# Some sane default values
534
if not -1 <= screen < xlib.XScreenCount(dpy):
535
raise RRError("The chosen screen is not available", screen)
537
self._screen = xlib.XDefaultScreen(dpy)
539
self._screen = screen
540
self._root = xlib.XDefaultRootWindow(self._display, self._screen)
541
self._id = rr.XRRRootToScreen(self._display, self._root)
543
self._load_resources()
545
(self._width, self._height,
546
self._width_mm, self._height_mm) = self.get_size()
547
if XRANDR_VERSION >= (1, 2):
548
self._load_screen_size_range()
552
# Store XRandR 1.0 changes here
553
self._rate = self.get_current_rate()
554
self._rotation = self.get_current_rotation()
555
self._size_index = self.get_current_size_index()
558
"""Free the reference to the interal screen config if the screen
560
rr.XRRFreeScreenConfigInfo(self._config)
562
def _load_config(self):
563
"""Loads the screen configuration. Only needed privately by the
565
class XRRScreenConfiguration(Structure):
566
" private to Xrandr "
568
gsi = rr.XRRGetScreenInfo
569
gsi.restype = POINTER(XRRScreenConfiguration)
570
self._config = gsi(self._display, self._root)
572
def _load_screen_size_range(self):
573
"""Detects the dimensionios of the screen"""
578
res = rr.XRRGetScreenSizeRange(self._display, self._root,
579
byref(minWidth), byref(minHeight),
580
byref(maxWidth), byref(maxHeight))
582
self._width_max = maxWidth.value
583
self._width_min = minWidth.value
584
self._height_max = maxHeight.value
585
self._height_min = minHeight.value
587
def _load_resources(self):
588
"""Loads the screen resources. Only needed privately for the
590
gsr = rr.XRRGetScreenResources
591
gsr.restype = POINTER(_XRRScreenResources)
592
self._resources = gsr(self._display, self._root)
594
def _load_crtcs(self):
595
"""Loads the available XRandR 1.2 crtcs (hardware pipes) of
597
gci = rr.XRRGetCrtcInfo
598
gci.restype = POINTER(_XRRCrtcInfo)
599
c = self._resources.contents.crtcs
600
for i in range(self._resources.contents.ncrtc):
601
xrrcrtcinfo = gci(self._display, self._resources, c[i])
602
self.crtcs.append(Crtc(xrrcrtcinfo, c[i], self))
604
def _load_outputs(self):
605
"""Loads the available XRandR 1.2 outputs of the screen"""
606
goi = rr.XRRGetOutputInfo
607
goi.restype = POINTER(_XRROutputInfo)
608
o = self._resources.contents.outputs
609
for i in range(self._resources.contents.noutput):
610
xrroutputinfo = goi(self._display, self._resources, o[i])
611
output = Output(xrroutputinfo, o[i], self)
612
self.outputs[xrroutputinfo.contents.name] = output
613
# Store the mode of the crtc in the output instance
614
crtc = self.get_crtc_by_xid(output.get_crtc())
616
output._mode = crtc._info.contents.mode
617
crtc.add_output(output)
620
"""Returns the current pixel and physical size of the screen"""
621
width = xlib.XDisplayWidth(self._display, self._screen)
622
width_mm = xlib.XDisplayWidthMM(self._display, self._screen)
623
height = xlib.XDisplayHeight(self._display, self._screen)
624
height_mm = xlib.XDisplayHeightMM(self._display, self._screen)
625
return width, height, width_mm, height_mm
627
def get_timestamp(self):
628
"""Creates a X timestamp that must be used when applying changes, since
629
they can be delayed"""
630
config_timestamp = Time()
631
rr.XRRTimes.restpye = c_ulong
632
return rr.XRRTimes(self._display, self._id, byref(config_timestamp))
634
def get_crtc_by_xid(self, xid):
635
"""Returns the crtc with the given xid or None"""
636
for crtc in self.crtcs:
641
def get_current_rate(self):
642
"""Returns the currently used refresh rate"""
643
_check_required_version((1, 0))
644
xccr = rr.XRRConfigCurrentRate
646
return xccr(self._config)
648
def get_available_rates_for_size_index(self, size_index):
649
"""Returns the refresh rates that are supported by the screen for
650
the given resolution. See get_available_sizes for the resolution to
651
which size_index points"""
652
_check_required_version((1, 0))
655
rr.XRRConfigRates.restype = POINTER(c_ushort)
656
_rates = rr.XRRConfigRates(self._config, size_index, byref(nrates))
657
for r in range(nrates.value):
658
rates.append(_rates[r])
661
def get_current_rotation(self):
662
"""Returns the currently used rotation. Can be RR_ROTATE_0,
663
RR_ROTATE_90, RR_ROTATE_180 or RR_ROTATE_270"""
664
_check_required_version((1, 0))
666
rr.XRRConfigRotations(self._config, byref(current))
669
def get_available_rotations(self):
670
"""Returns a binary flag that holds the available resolutions"""
671
_check_required_version((1, 0))
673
rotations = rr.XRRConfigRotations(self._config, byref(current))
676
def get_current_size_index(self):
677
"""Returns the position of the currently used resolution size in the
678
list of available resolutions. See get_available_sizes"""
679
_check_required_version((1, 0))
680
rotation = c_ushort()
681
size = rr.XRRConfigCurrentConfiguration(self._config,
685
def get_available_sizes(self):
686
"""Returns the available resolution sizes of the screen. The size
687
index points to the corresponding resolution of this list"""
688
_check_required_version((1, 0))
691
xcs = rr.XRRConfigSizes
692
xcs.restype = POINTER(_XRRScreenSize)
693
_sizes = xcs(self._config, byref(nsizes))
694
for r in range(nsizes.value):
695
sizes.append(_sizes[r])
698
def set_config(self, size_index, rate, rotation):
699
"""Configures the screen with the given resolution at the given size
700
index, rotation and refresh rate. To get in effect call
701
Screen.apply_config()"""
702
_check_required_version((1, 0))
703
self.set_size_index(size_index)
704
self.set_refresh_rate(rate)
705
self.set_rotation(rotation)
707
def set_size_index(self, index):
708
"""Sets the reoslution of the screen. To get in effect call
709
Screen.apply_config()"""
710
if index in range(len(self.get_available_sizes())):
711
self._size_index = index
713
raise RRError("There isn't any size associated "
714
"to the index %s" % index)
716
def set_rotation(self, rotation):
717
"""Sets the rotation of the screen. To get in effect call
718
Screen.apply_config()"""
719
if self.get_available_rotations() & rotation:
720
self._rotation = rotation
722
raise RRError("The chosen rotation is not supported")
724
def set_refresh_rate(self, rate):
725
"""Sets the refresh rate of the screen. To get in effect call
726
Screen.apply_config()"""
727
if rate in self.get_available_rates_for_size_index(self._size_index):
730
raise RRError("The chosen refresh rate %s is not "
733
def get_mode_by_xid(self, xid):
734
"""Returns the mode of the given xid"""
735
screen_modes = self._resources.contents.modes
736
for s in range(self._resources.contents.nmode):
737
if screen_modes[s].id == xid:
738
return screen_modes[s]
741
def get_output_by_name(self, name):
742
"""Returns the output of the screen with the given name or None"""
743
if name in self.outputs:
744
return self.outputs[name]
748
def get_output_by_id(self, id):
749
"""Returns the output of the screen with the given xid or None"""
750
for o in list(self.outputs.values()):
755
def print_info(self, verbose=False):
756
"""Prints some information about the detected screen and its outputs"""
757
_check_required_version((1, 0))
758
print("Screen %s: minimum %s x %s, current %s x %s, maximum %s x %s" %\
760
self._width_min, self._height_min,
761
self._width, self._height,
762
self._width_max, self._height_max))
763
print(" %smm x %smm" % (self._width_mm, self._height_mm))
764
print("Crtcs: %s" % len(self.crtcs))
766
print("Modes (%s):" % self._resources.contents.nmode)
767
modes = self._resources.contents.modes
768
for i in range(self._resources.contents.nmode):
769
print(" %s - %sx%s" % (modes[i].name,
773
print("Sizes @ Refresh Rates:")
774
for s in self.get_available_sizes():
775
print(" [%s] %s x %s @ %s" % (
776
i, s.width, s.height,
777
self.get_available_rates_for_size_index(i)))
780
rots = self.get_available_rotations()
781
if rots & RR_ROTATE_0:
783
if rots & RR_ROTATE_90:
785
if rots & RR_ROTATE_180:
787
if rots & RR_ROTATE_270:
791
for o in list(self.outputs.keys()):
792
output = self.outputs[o]
794
if output.is_connected():
795
print("(%smm x %smm)" % (output.get_physical_width(),
796
output.get_physical_height()))
797
modes = output.get_available_modes()
799
for m in range(len(modes)):
801
refresh = mode.dotClock / (mode.hTotal * mode.vTotal)
802
print(" [%s] %s x %s @ %s" % (m,
806
if mode.id == output._mode:
808
if m == output.get_preferred_mode():
812
rots = output.get_available_rotations()
813
if rots & RR_ROTATE_0:
815
if rots & RR_ROTATE_90:
817
if rots & RR_ROTATE_180:
819
if rots & RR_ROTATE_270:
823
print("(not connected)")
825
print(" Core properties:")
826
for (f, t) in output._info.contents._fields_:
828
f, getattr(output._info.contents, f)))
830
def get_outputs(self):
831
"""Returns the outputs of the screen"""
832
_check_required_version((1, 2))
833
return list(self.outputs.values())
835
def get_output_names(self):
836
_check_required_version((1, 2))
837
return list(self.outputs.keys())
839
def set_size(self, width, height, width_mm, height_mm):
840
"""Apply the given pixel and physical size to the screen"""
841
_check_required_version((1, 2))
842
# Check if we really need to apply the changes
843
if (width, height, width_mm, height_mm) == self.get_size():
845
rr.XRRSetScreenSize(self._display, self._root,
846
c_int(width), c_int(height),
847
c_int(width_mm), c_int(height_mm))
849
def apply_output_config(self):
850
"""Used for instantly applying RandR 1.2 changes"""
851
_check_required_version((1, 2))
852
self._arrange_outputs()
853
self._calculate_size()
854
self.set_size(self._width, self._height,
855
self._width_mm, self._height_mm)
857
# Assign all active outputs to crtcs
858
for output in list(self.outputs.values()):
859
if not output._mode or output._crtc:
861
for crtc in output.get_crtcs():
862
if crtc and crtc.supports_output(output):
863
crtc.add_output(output)
864
output._changes = output._changes | CHANGES_CRTC
867
#FIXME: Take a look at the pick_crtc code in xrandr.c
868
raise RRError("There is no matching crtc for the output")
870
# Apply stored changes of crtcs
871
for crtc in self.crtcs:
872
if crtc.has_changed():
875
def apply_config(self):
876
"""Used for instantly applying RandR 1.0 changes"""
877
_check_required_version((1, 0))
878
status = rr.XRRSetScreenConfigAndRate(self._display,
884
self.get_timestamp())
887
def _arrange_outputs(self):
888
"""Arrange all output positions according to their relative position"""
889
for output in self.get_outputs():
890
# Skip not changed and not used outputs
891
if not output.has_changed(CHANGES_RELATION) or \
892
output._mode == None:
894
relative = output._relative_to
895
mode = self.get_mode_by_xid(output._mode)
896
mode_relative = self.get_mode_by_xid(relative._mode)
897
if not relative or not relative._mode:
900
output._changes = output._changes | CHANGES_POSITION
901
if output._relation == RELATION_LEFT_OF:
902
output._y = relative._y + output._relation_offset
903
output._x = relative._x - \
904
get_mode_width(mode, output._rotation)
905
elif output._relation == RELATION_RIGHT_OF:
906
output._y = relative._y + output._relation_offset
907
output._x = relative._x + get_mode_width(mode_relative,
909
elif output._relation == RELATION_ABOVE:
910
output._y = relative._y - get_mode_height(mode,
912
output._x = relative._x + output._relation_offset
913
elif output._relation == RELATION_BELOW:
914
output._y = relative._y + get_mode_height(mode_relative,
916
output._x = relative._x + output._relation_offset
917
elif output._relation == RELATION_SAME_AS:
918
output._y = relative._y + output._relation_offset
919
output._x = relative._x + output._relation_offset
920
output._changes = output._changes | CHANGES_POSITION
921
# Normalize the postions so to the upper left cornor of all outputs
925
for output in self.get_outputs():
926
if output._mode == None:
928
if output._x < min_x:
930
if output._y < min_y:
932
for output in self.get_outputs():
933
if output._mode == None:
937
output._changes = output._changes | CHANGES_POSITION
939
def _calculate_size(self):
940
"""Recalculate the pixel and physical size of the screen so that
941
it covers all outputs"""
943
height = self._height
944
for output in self.get_outputs():
947
mode = self.get_mode_by_xid(output._mode)
950
w = get_mode_width(mode, output._rotation)
951
h = get_mode_height(mode, output._rotation)
956
if width > self._width_max or height > self._height_max:
957
raise RRError("The required size is not supported",
958
(width, height), (self._width_max, self._width_min))
960
if height < self._height_min:
961
self._fb_height = self._height_min
963
self._height = height
964
if width < self._width_min:
965
self._width = self._width_min
968
#FIXME: Physical size is missing
971
def get_current_display():
972
"""Returns the currently used display"""
973
display_url = os.getenv("DISPLAY")
974
open_display = xlib.XOpenDisplay
975
# Set .argtypes and .restype, to ensure proper
976
# type check and conversion
977
open_display.restype = c_void_p
978
open_display.argtypes = [c_char_p]
979
# XOpenDisplay accepts a char*, but
980
# display_url is a unicode string therefore
981
# we convert it to a bytes string
982
dpy = open_display(display_url.encode('utf-8'))
986
def get_current_screen():
987
"""Returns the currently used screen"""
988
screen = Screen(get_current_display())
992
def get_screen_of_display(display, count):
993
"""Returns the screen of the given display"""
994
dpy = xlib.XOpenDisplay(display)
995
return Screen(dpy, count)
999
"""Returns a tuple containing the major and minor version of the xrandr
1000
extension or None if the extension is not available"""
1003
res = rr.XRRQueryVersion(get_current_display(),
1004
byref(major), byref(minor))
1006
return (major.value, minor.value)
1010
def has_extension():
1011
"""Returns True if the xrandr extension is available"""
1017
def _to_gamma(gamma):
1018
g = rr.XRRAllocGamma(len(gamma[0]))
1019
for i in range(gamma[0]):
1020
g.red[i] = gamma[0][i]
1021
g.green[i] = gamma[1][i]
1022
g.blue[i] = gamma[2][i]
1027
gamma = ([], [], [])
1028
for i in range(g.size):
1029
gamma[0].append(g.red[i])
1030
gamma[1].append(g.green[i])
1031
gamma[2].append(g.blue[i])
1035
def _check_required_version(version):
1036
"""Raises an exception if the given or a later version of xrandr is not
1038
if XRANDR_VERSION == None or XRANDR_VERSION < version:
1039
raise UnsupportedRRError(version, XRANDR_VERSION)
1042
def get_mode_height(mode, rotation):
1043
"""Return the height of the given mode taking the rotation into account"""
1044
if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
1046
elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
1052
def get_mode_width(mode, rotation):
1053
"""Return the width of the given mode taking the rotation into account"""
1054
if rotation & (RR_ROTATE_0 | RR_ROTATE_180):
1056
elif rotation & (RR_ROTATE_90 | RR_ROTATE_270):
1062
XRANDR_VERSION = get_version()