Skip to content

Commit fef556e

Browse files
author
BoboTiG
committed
Fully functionnal on GNU/Linux using Xrandr
1 parent df43057 commit fef556e

File tree

1 file changed

+56
-85
lines changed

1 file changed

+56
-85
lines changed

mss.py

Lines changed: 56 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
If that URL should fail, try contacting the author.
1212
'''
1313

14-
from __future__ import (unicode_literals, absolute_import, division,
15-
print_function)
14+
from __future__ import (unicode_literals, print_function)
1615

1716
__version__ = '0.0.8'
1817
__author__ = "Mickaël 'Tiger-222' Schoentgen"
@@ -45,11 +44,9 @@ class ScreenshotError(Exception):
4544
from LaunchServices import kUTTypePNG
4645
elif system() == 'Linux':
4746
from os import environ
48-
from os.path import expanduser
49-
import xml.etree.ElementTree as ET
5047
from ctypes.util import find_library
51-
from ctypes import byref, cast, cdll, POINTER, Structure, \
52-
c_char_p, c_int, c_int32, c_uint, c_uint32, c_ulong, c_void_p
48+
from ctypes import byref, cast, cdll, POINTER, Structure, c_char_p,\
49+
c_int, c_int32, c_long, c_uint, c_uint32, c_ulong, c_ushort, c_void_p
5350

5451
class Display(Structure):
5552
pass
@@ -76,6 +73,22 @@ class XImage(Structure):
7673
('bits_per_pixel', c_int), ('red_mask', c_ulong),
7774
('green_mask', c_ulong), ('blue_mask', c_ulong)]
7875

76+
class XRRModeInfo(Structure):
77+
pass
78+
79+
class XRRScreenResources(Structure):
80+
_fields_ = [("timestamp", c_ulong), ("configTimestamp", c_ulong),
81+
("ncrtc", c_int), ("crtcs", POINTER(c_long)),
82+
("noutput", c_int), ("outputs", POINTER(c_long)),
83+
("nmode", c_int), ("modes", POINTER(XRRModeInfo))]
84+
85+
class XRRCrtcInfo(Structure):
86+
_fields_ = [("timestamp", c_ulong), ("x", c_int), ("y", c_int),
87+
("width", c_int), ("height", c_int), ("mode", c_long),
88+
("rotation", c_int), ("noutput", c_int),
89+
("outputs", POINTER(c_long)), ("rotations", c_ushort),
90+
("npossible", c_int), ("possible", POINTER(c_long))]
91+
7992
def b(x):
8093
return pack(b'<B', x)
8194
elif system() == 'Windows':
@@ -352,10 +365,15 @@ def init(self):
352365
x11 = find_library('X11')
353366
if not x11:
354367
raise ScreenshotError('No X11 library found.')
355-
356368
self.xlib = cdll.LoadLibrary(x11)
357369
self.debug('init', 'xlib', self.xlib)
358370

371+
xrandr = find_library('Xrandr')
372+
if not xrandr:
373+
raise ScreenshotError('No Xrandr library found.')
374+
self.xrandr = cdll.LoadLibrary(xrandr)
375+
self.debug('init', 'xrandr', self.xrandr)
376+
359377
self._set_argtypes()
360378
self._set_restypes()
361379

@@ -398,6 +416,15 @@ def _set_argtypes(self):
398416
self.xlib.XGetPixel.argtypes = [POINTER(XImage), c_int, c_int]
399417
self.xlib.XFree.argtypes = [POINTER(XImage)]
400418
self.xlib.XCloseDisplay.argtypes = [POINTER(Display)]
419+
self.xrandr.XRRGetScreenResources.argtypes = [POINTER(Display),
420+
POINTER(Display)]
421+
self.xrandr.XRRGetCrtcInfo.argtypes = [POINTER(Display),
422+
POINTER(XRRScreenResources),
423+
c_long]
424+
self.xrandr.XRRFreeScreenResources.argtypes = [
425+
POINTER(XRRScreenResources)
426+
]
427+
self.xrandr.XRRFreeCrtcInfo.argtypes = [POINTER(XRRCrtcInfo)]
401428

402429
def _set_restypes(self):
403430
''' Functions return type '''
@@ -406,84 +433,17 @@ def _set_restypes(self):
406433

407434
self.xlib.XOpenDisplay.restype = POINTER(Display)
408435
self.xlib.XDefaultScreen.restype = c_int
409-
self.xlib.XDefaultRootWindow.restype = POINTER(XWindowAttributes)
436+
self.xrandr.XRRGetCrtcInfo.restype = POINTER(XRRCrtcInfo)
410437
self.xlib.XGetWindowAttributes.restype = c_int
411438
self.xlib.XAllPlanes.restype = c_ulong
412439
self.xlib.XGetImage.restype = POINTER(XImage)
413440
self.xlib.XGetPixel.restype = c_ulong
414441
self.xlib.XFree.restype = c_void_p
415442
self.xlib.XCloseDisplay.restype = c_void_p
416-
417-
def _x11_config(self):
418-
''' Try to determine display monitors from X11 configuration file:
419-
~/.config/monitors.xml
420-
'''
421-
422-
self.debug('_x11_config')
423-
424-
monitors = expanduser('~/.config/monitors.xml')
425-
if not os.path.isfile(monitors):
426-
self.debug('ERROR', 'MSSLinux: _x11_config() failed.')
427-
return
428-
429-
tree = ET.parse(monitors)
430-
root = tree.getroot()
431-
config = root.findall('configuration')[-1]
432-
conf = []
433-
for output in config.findall('output'):
434-
name = output.get('name')
435-
if name != 'default':
436-
x = output.find('x')
437-
y = output.find('y')
438-
width = output.find('width')
439-
height = output.find('height')
440-
rotation = output.find('rotation')
441-
if None not in [x, y, width, height] and name not in conf:
442-
conf.append(name)
443-
if rotation.text in ['left', 'right']:
444-
width, height = height, width
445-
yield ({
446-
b'left': int(x.text),
447-
b'top': int(y.text),
448-
b'width': int(width.text),
449-
b'height': int(height.text),
450-
b'rotation': rotation.text
451-
})
452-
453-
def _xfce4_config(self):
454-
''' Try to determine display monitors from XFCE4 configuration file:
455-
~/.config/xfce4/xfconf/xfce-perchannel-xml/displays.xml
456-
'''
457-
458-
self.debug('_xfce4_config')
459-
460-
path_ = '~/.config/xfce4/xfconf/xfce-perchannel-xml/displays.xml'
461-
monitors = expanduser(path_)
462-
if not os.path.isfile(monitors):
463-
self.debug('ERROR', 'MSSLinux: _xfce4_config() failed.')
464-
return
465-
466-
rotations = {0: 'normal', 90: 'left', 270: 'right'}
467-
tree = ET.parse(monitors)
468-
root = tree.getroot()
469-
config = root.findall('property')[0]
470-
for output in config.findall('property'):
471-
name = output.get('name')
472-
if name != 'default':
473-
active, res, _, rot, _, _, pos = output.findall('property')
474-
if active.get('value') == 'true':
475-
width, height = res.get('value').split('x')
476-
rotation = rotations[int(rot.get('value'))]
477-
if rotation in ['left', 'right']:
478-
width, height = height, width
479-
posx, posy = pos.findall('property')
480-
yield ({
481-
b'left': int(posx.get('value')),
482-
b'top': int(posy.get('value')),
483-
b'width': int(width),
484-
b'height': int(height),
485-
b'rotation': rotation
486-
})
443+
self.xlib.XDefaultRootWindow.restype = POINTER(XWindowAttributes)
444+
self.xrandr.XRRGetScreenResources.restype = POINTER(XRRScreenResources)
445+
self.xrandr.XRRFreeScreenResources.restype = c_void_p
446+
self.xrandr.XRRFreeCrtcInfo.restype = c_void_p
487447

488448
def enum_display_monitors(self, screen=0):
489449
''' Get positions of one or more monitors.
@@ -502,12 +462,23 @@ def enum_display_monitors(self, screen=0):
502462
b'height': int(gwa.height)
503463
})
504464
else:
505-
# It is a little more complicated, we have to guess all stuff
506-
# from differents XML configuration files.
507-
for config in ['x11', 'xfce4']:
508-
for mon in getattr(self, '_{}_config'.format(config))():
509-
if mon:
510-
yield mon
465+
# Fix for XRRGetScreenResources:
466+
# expected LP_Display instance instead of LP_XWindowAttributes
467+
root = cast(self.root, POINTER(Display))
468+
mon = self.xrandr.XRRGetScreenResources(self.display, root)
469+
self.debug('enum_display_monitors', 'number of monitors',
470+
mon.contents.noutput - 1)
471+
for num in range(mon.contents.noutput - 1):
472+
crtc_info = self.xrandr.XRRGetCrtcInfo(self.display, mon,
473+
mon.contents.crtcs[num])
474+
yield ({
475+
b'left': int(crtc_info.contents.x),
476+
b'top': int(crtc_info.contents.y),
477+
b'width': int(crtc_info.contents.width),
478+
b'height': int(crtc_info.contents.height)
479+
})
480+
self.xrandr.XRRFreeCrtcInfo(crtc_info)
481+
self.xrandr.XRRFreeScreenResources(mon)
511482

512483
def get_pixels(self, monitor):
513484
''' Retrieve all pixels from a monitor. Pixels have to be RGB.

0 commit comments

Comments
 (0)