@@ -45,7 +45,8 @@ class ScreenshotError(Exception):
4545 from os import environ
4646 from ctypes .util import find_library
4747 from ctypes import byref , cast , cdll , POINTER , Structure , c_char_p ,\
48- c_int , c_int32 , c_long , c_uint , c_uint32 , c_ulong , c_ushort , c_void_p
48+ c_int , c_int32 , c_long , c_uint , c_uint32 , c_ulong , c_ushort , c_void_p , \
49+ create_string_buffer
4950
5051 class Display (Structure ):
5152 pass
@@ -144,8 +145,6 @@ def get_pixels(self, monitor):
144145 'width': the width,
145146 'heigth': the height
146147 }
147-
148- Returns a dict of pixels.
149148 '''
150149 raise NotImplementedError ('MSS: subclasses need to implement this!' )
151150
@@ -324,6 +323,13 @@ def __init__(self):
324323 raise ScreenshotError ('MSS: no Xrandr library found.' )
325324 self .xrandr = cdll .LoadLibrary (xrandr )
326325
326+ mss = find_library ('libmss.so' )
327+ self .mss = False
328+ if mss :
329+ self .mss = cdll .LoadLibrary (mss )
330+ else :
331+ print ('MSS: no MSS library found. Using native function (slow).' )
332+
327333 self ._set_argtypes ()
328334 self ._set_restypes ()
329335
@@ -363,6 +369,10 @@ def _set_argtypes(self):
363369 POINTER (XRRScreenResources )
364370 ]
365371 self .xrandr .XRRFreeCrtcInfo .argtypes = [POINTER (XRRCrtcInfo )]
372+ if self .mss :
373+ self .mss .GetXImagePixels .argtypes = [POINTER (XImage ), c_uint ,
374+ c_uint , c_uint , c_uint ,
375+ c_uint , c_void_p ]
366376
367377 def _set_restypes (self ):
368378 ''' Functions return type.
@@ -385,6 +395,8 @@ def _set_restypes(self):
385395 self .xrandr .XRRGetCrtcInfo .restype = POINTER (XRRCrtcInfo )
386396 self .xrandr .XRRFreeScreenResources .restype = c_void_p
387397 self .xrandr .XRRFreeCrtcInfo .restype = c_void_p
398+ if self .mss :
399+ self .mss .GetXImagePixels .restype = c_void_p
388400
389401 def enum_display_monitors (self , screen = 0 ):
390402 ''' Get positions of one or more monitors.
@@ -394,12 +406,10 @@ def enum_display_monitors(self, screen=0):
394406 if screen == - 1 :
395407 gwa = XWindowAttributes ()
396408 self .xlib .XGetWindowAttributes (self .display , self .root , byref (gwa ))
397- yield ({
398- b'left' : int (gwa .x ),
399- b'top' : int (gwa .y ),
400- b'width' : int (gwa .width ),
401- b'height' : int (gwa .height )
402- })
409+ yield {b'left' : int (gwa .x ),
410+ b'top' : int (gwa .y ),
411+ b'width' : int (gwa .width ),
412+ b'height' : int (gwa .height )}
403413 else :
404414 # Fix for XRRGetScreenResources:
405415 # expected LP_Display instance instead of LP_XWindowAttributes
@@ -408,18 +418,51 @@ def enum_display_monitors(self, screen=0):
408418 for num in range (mon .contents .ncrtc ):
409419 crtc = self .xrandr .XRRGetCrtcInfo (self .display , mon ,
410420 mon .contents .crtcs [num ])
411- yield ({
412- b'left' : int (crtc .contents .x ),
413- b'top' : int (crtc .contents .y ),
414- b'width' : int (crtc .contents .width ),
415- b'height' : int (crtc .contents .height )
416- })
421+ yield {b'left' : int (crtc .contents .x ),
422+ b'top' : int (crtc .contents .y ),
423+ b'width' : int (crtc .contents .width ),
424+ b'height' : int (crtc .contents .height )}
417425 self .xrandr .XRRFreeCrtcInfo (crtc )
418426 self .xrandr .XRRFreeScreenResources (mon )
419427
420428 def get_pixels (self , monitor ):
429+ ''' Retrieve all pixels from a monitor. Pixels have to be RGB. '''
430+
431+ width , height = monitor [b'width' ], monitor [b'height' ]
432+ left , top = monitor [b'left' ], monitor [b'top' ]
433+ ZPixmap = 2
434+ allplanes = self .xlib .XAllPlanes ()
435+
436+ # Fix for XGetImage:
437+ # expected LP_Display instance instead of LP_XWindowAttributes
438+ root = cast (self .root , POINTER (Display ))
439+
440+ ximage = self .xlib .XGetImage (self .display , root , left , top , width ,
441+ height , allplanes , ZPixmap )
442+ if not ximage :
443+ raise ScreenshotError ('MSS: XGetImage() failed.' )
444+
445+ if not self .mss :
446+ self .image = self .get_pixels_slow (ximage , width , height ,
447+ ximage .contents .red_mask ,
448+ ximage .contents .green_mask ,
449+ ximage .contents .blue_mask )
450+ else :
451+ buffer_len = height * width * 3
452+ self .image = create_string_buffer (buffer_len )
453+ self .mss .GetXImagePixels (ximage , width , height ,
454+ ximage .contents .red_mask ,
455+ ximage .contents .green_mask ,
456+ ximage .contents .blue_mask ,
457+ self .image )
458+ self .xlib .XDestroyImage (ximage )
459+ return self .image
460+
461+ def get_pixels_slow (self , ximage , width , height , rmask , gmask , bmask ):
421462 ''' Retrieve all pixels from a monitor. Pixels have to be RGB.
422463
464+ /!\ Insanely slow version using ctypes.
465+
423466 The XGetPixel() C code can be found at this URL:
424467 http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/ImUtil.c#n444
425468
@@ -451,29 +494,15 @@ def get_pixels(self, monitor):
451494 self.image = create_string_buffer(sizeof(c_char) * buffer_len)
452495 for x in range(width):
453496 for y in range(height):
454- offset = width * y * 3
497+ offset = x * 3 + width * y * 3
455498 addr = data[y * bpl + (x << 2)][0]
456499 pixel = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]
457- self.image[x * 3 + offset] = (pixel & rmask) >> 16
458- self.image[x * 3 + offset + 1] = (pixel & gmask) >> 8
459- self.image[x * 3 + offset + 2] = pixel & bmask
500+ self.image[offset] = (pixel & rmask) >> 16
501+ self.image[offset + 1] = (pixel & gmask) >> 8
502+ self.image[offset + 2] = pixel & bmask
460503 return self.image
461504 '''
462505
463- width , height = monitor [b'width' ], monitor [b'height' ]
464- left , top = monitor [b'left' ], monitor [b'top' ]
465- ZPixmap = 2
466- allplanes = self .xlib .XAllPlanes ()
467-
468- # Fix for XGetImage:
469- # expected LP_Display instance instead of LP_XWindowAttributes
470- root = cast (self .root , POINTER (Display ))
471-
472- ximage = self .xlib .XGetImage (self .display , root , left , top , width ,
473- height , allplanes , ZPixmap )
474- if not ximage :
475- raise ScreenshotError ('MSS: XGetImage() failed.' )
476-
477506 # @TODO: this part takes most of the time. Need a better solution.
478507 def pix (pixel , _resultats = {}, _b = pack ):
479508 ''' Apply shifts to a pixel to get the RGB values.
@@ -484,14 +513,10 @@ def pix(pixel, _resultats={}, _b=pack):
484513 _b (b'<B' , (pixel & gmask ) >> 8 ) + _b (b'<B' , pixel & bmask )
485514 return _resultats [pixel ]
486515
487- rmask = ximage .contents .red_mask
488- gmask = ximage .contents .green_mask
489- bmask = ximage .contents .blue_mask
490516 get_pix = self .xlib .XGetPixel
491517 pixels = [pix (get_pix (ximage , x , y ))
492518 for y in range (height ) for x in range (width )]
493519 self .image = b'' .join (pixels )
494- self .xlib .XDestroyImage (ximage )
495520 return self .image
496521
497522
@@ -548,25 +573,21 @@ def enum_display_monitors(self, screen=-1):
548573 right = windll .user32 .GetSystemMetrics (SM_CXVIRTUALSCREEN )
549574 top = windll .user32 .GetSystemMetrics (SM_YVIRTUALSCREEN )
550575 bottom = windll .user32 .GetSystemMetrics (SM_CYVIRTUALSCREEN )
551- yield ({
552- b'left' : int (left ),
553- b'top' : int (top ),
554- b'width' : int (right - left ),
555- b'height' : int (bottom - top )
556- })
576+ yield {b'left' : int (left ),
577+ b'top' : int (top ),
578+ b'width' : int (right - left ),
579+ b'height' : int (bottom - top )}
557580 else :
558581
559582 def _callback (monitor , dc , rect , data ):
560583 ''' Callback for MONITORENUMPROC() function, it will return
561584 a RECT with appropriate values.
562585 '''
563586 rct = rect .contents
564- monitors .append ({
565- b'left' : int (rct .left ),
566- b'top' : int (rct .top ),
567- b'width' : int (rct .right - rct .left ),
568- b'height' : int (rct .bottom - rct .top )
569- })
587+ monitors .append ({b'left' : int (rct .left ),
588+ b'top' : int (rct .top ),
589+ b'width' : int (rct .right - rct .left ),
590+ b'height' : int (rct .bottom - rct .top )})
570591 return 1
571592
572593 monitors = []
0 commit comments