11#!/usr/bin/env python
22# -*- coding: utf-8 -*-
3-
43''' A cross-platform multi-screen shot module in pure python using ctypes.
54
65 This module is maintained by Mickaël Schoentgen <mickael@jmsinfo.co>.
1211 If that URL should fail, try contacting the author.
1312'''
1413
15- from __future__ import (unicode_literals , absolute_import ,
16- division , print_function )
14+ from __future__ import (unicode_literals , absolute_import , division ,
15+ print_function )
1716
1817__version__ = '0.0.8'
1918__author__ = "Mickaël 'Tiger-222' Schoentgen"
2928'''
3029__all__ = ['MSSLinux' , 'MSSMac' , 'MSSWindows' ]
3130
32-
3331from struct import pack
3432from platform import system
3533import os .path
@@ -41,6 +39,7 @@ class ScreenshotError(Exception):
4139 ''' Error handling class. '''
4240 pass
4341
42+
4443if system () == 'Darwin' :
4544 from Quartz import *
4645 from LaunchServices import kUTTypePNG
@@ -56,50 +55,26 @@ class Display(Structure):
5655 pass
5756
5857 class XWindowAttributes (Structure ):
59- _fields_ = [
60- ('x' , c_int32 ),
61- ('y' , c_int32 ),
62- ('width' , c_int32 ),
63- ('height' , c_int32 ),
64- ('border_width' , c_int32 ),
65- ('depth' , c_int32 ),
66- ('visual' , c_ulong ),
67- ('root' , c_ulong ),
68- ('class' , c_int32 ),
69- ('bit_gravity' , c_int32 ),
70- ('win_gravity' , c_int32 ),
71- ('backing_store' , c_int32 ),
72- ('backing_planes' , c_ulong ),
73- ('backing_pixel' , c_ulong ),
74- ('save_under' , c_int32 ),
75- ('colourmap' , c_ulong ),
76- ('mapinstalled' , c_uint32 ),
77- ('map_state' , c_uint32 ),
78- ('all_event_masks' , c_ulong ),
79- ('your_event_mask' , c_ulong ),
80- ('do_not_propagate_mask' , c_ulong ),
81- ('override_redirect' , c_int32 ),
82- ('screen' , c_ulong )
83- ]
58+ _fields_ = [('x' , c_int32 ), ('y' , c_int32 ), ('width' , c_int32 ),
59+ ('height' , c_int32 ), ('border_width' , c_int32 ),
60+ ('depth' , c_int32 ), ('visual' , c_ulong ), ('root' , c_ulong ),
61+ ('class' , c_int32 ), ('bit_gravity' , c_int32 ),
62+ ('win_gravity' , c_int32 ), ('backing_store' , c_int32 ),
63+ ('backing_planes' , c_ulong ), ('backing_pixel' , c_ulong ),
64+ ('save_under' , c_int32 ), ('colourmap' , c_ulong ),
65+ ('mapinstalled' , c_uint32 ), ('map_state' , c_uint32 ),
66+ ('all_event_masks' , c_ulong ), ('your_event_mask' , c_ulong ),
67+ ('do_not_propagate_mask' , c_ulong ),
68+ ('override_redirect' , c_int32 ), ('screen' , c_ulong )]
8469
8570 class XImage (Structure ):
86- _fields_ = [
87- ('width' , c_int ),
88- ('height' , c_int ),
89- ('xoffset' , c_int ),
90- ('format' , c_int ),
91- ('data' , c_char_p ),
92- ('byte_order' , c_int ),
93- ('bitmap_unit' , c_int ),
94- ('bitmap_bit_order' , c_int ),
95- ('bitmap_pad' , c_int ),
96- ('depth' , c_int ),
97- ('bytes_per_line' , c_int ),
98- ('bits_per_pixel' , c_int ),
99- ('red_mask' , c_ulong ),
100- ('green_mask' , c_ulong ),
101- ('blue_mask' , c_ulong )
102- ]
71+ _fields_ = [('width' , c_int ), ('height' , c_int ), ('xoffset' , c_int ),
72+ ('format' , c_int ), ('data' , c_char_p ),
73+ ('byte_order' , c_int ), ('bitmap_unit' , c_int ),
74+ ('bitmap_bit_order' , c_int ), ('bitmap_pad' , c_int ),
75+ ('depth' , c_int ), ('bytes_per_line' , c_int ),
76+ ('bits_per_pixel' , c_int ), ('red_mask' , c_ulong ),
77+ ('green_mask' , c_ulong ), ('blue_mask' , c_ulong )]
10378
10479 def b (x ):
10580 return pack (b'<B' , x )
@@ -110,30 +85,21 @@ def b(x):
11085 HGDIOBJ , HWND , INT , LPARAM , LONG , RECT , UINT , WORD
11186
11287 class BITMAPINFOHEADER (Structure ):
113- _fields_ = [
114- ('biSize' , DWORD ),
115- ('biWidth' , LONG ),
116- ('biHeight' , LONG ),
117- ('biPlanes' , WORD ),
118- ('biBitCount' , WORD ),
119- ('biCompression' , DWORD ),
120- ('biSizeImage' , DWORD ),
121- ('biXPelsPerMeter' , LONG ),
122- ('biYPelsPerMeter' , LONG ),
123- ('biClrUsed' , DWORD ),
124- ('biClrImportant' , DWORD )
125- ]
88+ _fields_ = [('biSize' , DWORD ), ('biWidth' , LONG ), ('biHeight' , LONG ),
89+ ('biPlanes' , WORD ), ('biBitCount' , WORD ),
90+ ('biCompression' , DWORD ), ('biSizeImage' , DWORD ),
91+ ('biXPelsPerMeter' , LONG ), ('biYPelsPerMeter' , LONG ),
92+ ('biClrUsed' , DWORD ), ('biClrImportant' , DWORD )]
12693
12794 class BITMAPINFO (Structure ):
128- _fields_ = [
129- ('bmiHeader' , BITMAPINFOHEADER ),
130- ('bmiColors' , DWORD * 3 )
131- ]
95+ _fields_ = [('bmiHeader' , BITMAPINFOHEADER ), ('bmiColors' , DWORD * 3 )]
13296
13397 if sys .version < '3' :
98+
13499 def b (x ):
135100 return x
136101 else :
102+
137103 def b (x ):
138104 return pack (b'<B' , x )
139105else :
@@ -166,8 +132,7 @@ def debug(self, method='', scalar=None, value=None):
166132 print (':: {}()' .format (method ))
167133 else :
168134 print ('{}() {} {} {}' .format (method , scalar ,
169- type (value ).__name__ ,
170- value ))
135+ type (value ).__name__ , value ))
171136
172137 def enum_display_monitors (self ):
173138 ''' Get positions of all monitors.
@@ -205,8 +170,7 @@ def get_pixels(self, monitor_infos):
205170 def save (self ,
206171 output = 'screenshot-%d.png' ,
207172 screen = 0 ,
208- callback = lambda * x : True
209- ):
173+ callback = lambda * x : True ):
210174 ''' For each monitor, grab a screen shot and save it to a file.
211175
212176 Parameters:
@@ -231,10 +195,10 @@ def save(self,
231195 # Monitors screen shots!
232196 for i , monitor in enumerate (self .enum_display_monitors (screen )):
233197 self .debug ('save' , 'monitor' , monitor )
234- if screen <= 0 or (screen > 0 and i + 1 == screen ):
198+ if screen <= 0 or (screen > 0 and i + 1 == screen ):
235199 fname = output
236200 if '%d' in output :
237- fname = output .replace ('%d' , str (i + 1 ))
201+ fname = output .replace ('%d' , str (i + 1 ))
238202 self .debug ('save' , 'fname' , fname )
239203 if os .path .isfile (fname ) and not callback (fname ):
240204 continue
@@ -256,9 +220,8 @@ def save_img(self, data, width, height, output):
256220 to_take = (width * 3 + 3 ) & - 4
257221 padding = 0 if to_take % 8 == 0 else (to_take % 8 ) // 2
258222 scanlines = b'' .join (
259- [b'0' + data [(y * to_take ):(y * to_take )+ to_take - padding ]
260- for y in range (height )]
261- )
223+ [b'0' + data [(y * to_take ):(y * to_take ) + to_take - padding ]
224+ for y in range (height )])
262225
263226 magic = pack (b'>8B' , 137 , 80 , 78 , 71 , 13 , 10 , 26 , 10 )
264227
@@ -281,8 +244,7 @@ def save_img(self, data, width, height, output):
281244
282245 with open (output , 'wb' ) as fileh :
283246 fileh .write (
284- magic + b'' .join (ihdr ) + b'' .join (idat ) + b'' .join (iend )
285- )
247+ magic + b'' .join (ihdr ) + b'' .join (idat ) + b'' .join (iend ))
286248 if not os .path .isfile (output ):
287249 msg = 'Impossible to write data to file "{}".' .format (output )
288250 raise ScreenshotError (msg )
@@ -311,7 +273,7 @@ def enum_display_monitors(self, screen=0):
311273 b'top' : int (rect .origin .y ),
312274 b'width' : int (rect .size .width ),
313275 b'height' : int (rect .size .height )
314- })
276+ })
315277 else :
316278 max_displays = 32 # Could be augmented, if needed ...
317279 rotations = {0.0 : 'normal' , 90.0 : 'right' , - 90.0 : 'left' }
@@ -330,7 +292,7 @@ def enum_display_monitors(self, screen=0):
330292 b'width' : int (width ),
331293 b'height' : int (height ),
332294 b'rotation' : rotation
333- })
295+ })
334296
335297 def get_pixels (self , monitor ):
336298 ''' Retrieve all pixels from a monitor. Pixels have to be RGB.
@@ -341,9 +303,8 @@ def get_pixels(self, monitor):
341303 width , height = monitor [b'width' ], monitor [b'height' ]
342304 left , top = monitor [b'left' ], monitor [b'top' ]
343305 rect = CGRect ((left , top ), (width , height ))
344- image = CGWindowListCreateImage (
345- rect , kCGWindowListOptionOnScreenOnly ,
346- kCGNullWindowID , kCGWindowImageDefault )
306+ image = CGWindowListCreateImage (rect , kCGWindowListOptionOnScreenOnly ,
307+ kCGNullWindowID , kCGWindowImageDefault )
347308 if not image :
348309 raise ScreenshotError ('CGWindowListCreateImage() failed.' )
349310 return image
@@ -427,8 +388,8 @@ def _set_argtypes(self):
427388 POINTER (XWindowAttributes )]
428389 self .xlib .XAllPlanes .argtypes = []
429390 self .xlib .XGetImage .argtypes = [POINTER (Display ), POINTER (Display ),
430- c_int , c_int , c_uint , c_uint ,
431- c_ulong , c_int ]
391+ c_int , c_int , c_uint , c_uint , c_ulong ,
392+ c_int ]
432393 self .xlib .XGetPixel .argtypes = [POINTER (XImage ), c_int , c_int ]
433394 self .xlib .XFree .argtypes = [POINTER (XImage )]
434395 self .xlib .XCloseDisplay .argtypes = [POINTER (Display )]
@@ -482,7 +443,7 @@ def _x11_config(self):
482443 b'width' : int (width .text ),
483444 b'height' : int (height .text ),
484445 b'rotation' : rotation .text
485- })
446+ })
486447
487448 def _xfce4_config (self ):
488449 ''' Try to determine display monitors from XFCE4 configuration file:
@@ -517,7 +478,7 @@ def _xfce4_config(self):
517478 b'width' : int (width ),
518479 b'height' : int (height ),
519480 b'rotation' : rotation
520- })
481+ })
521482
522483 def enum_display_monitors (self , screen = 0 ):
523484 ''' Get positions of one or more monitors.
@@ -534,7 +495,7 @@ def enum_display_monitors(self, screen=0):
534495 b'top' : int (gwa .y ),
535496 b'width' : int (gwa .width ),
536497 b'height' : int (gwa .height )
537- })
498+ })
538499 else :
539500 # It is a little more complicated, we have to guess all stuff
540501 # from differents XML configuration files.
@@ -600,8 +561,8 @@ def _set_argtypes(self):
600561
601562 self .debug ('_set_argtypes' )
602563
603- self .MONITORENUMPROC = WINFUNCTYPE (INT , DWORD , DWORD ,
604- POINTER ( RECT ), DOUBLE )
564+ self .MONITORENUMPROC = WINFUNCTYPE (INT , DWORD , DWORD , POINTER ( RECT ),
565+ DOUBLE )
605566 windll .user32 .GetSystemMetrics .argtypes = [INT ]
606567 windll .user32 .EnumDisplayMonitors .argtypes = [HDC , c_void_p ,
607568 self .MONITORENUMPROC ,
@@ -610,8 +571,8 @@ def _set_argtypes(self):
610571 windll .gdi32 .CreateCompatibleDC .argtypes = [HDC ]
611572 windll .gdi32 .CreateCompatibleBitmap .argtypes = [HDC , INT , INT ]
612573 windll .gdi32 .SelectObject .argtypes = [HDC , HGDIOBJ ]
613- windll .gdi32 .BitBlt .argtypes = [HDC , INT , INT , INT , INT , HDC ,
614- INT , INT , DWORD ]
574+ windll .gdi32 .BitBlt .argtypes = [HDC , INT , INT , INT , INT , HDC , INT , INT ,
575+ DWORD ]
615576 windll .gdi32 .DeleteObject .argtypes = [HGDIOBJ ]
616577 windll .gdi32 .GetDIBits .argtypes = [HDC , HBITMAP , UINT , UINT , c_void_p ,
617578 POINTER (BITMAPINFO ), UINT ]
@@ -650,8 +611,9 @@ def enum_display_monitors(self, screen=-1):
650611 b'top' : int (top ),
651612 b'width' : int (right - left ),
652613 b'height' : int (bottom - top )
653- })
614+ })
654615 else :
616+
655617 def _callback (monitor , dc , rect , data ):
656618 ''' Callback for MONITORENUMPROC() function, it will return
657619 a RECT with appropriate values.
@@ -662,7 +624,7 @@ def _callback(monitor, dc, rect, data):
662624 b'top' : int (rct .top ),
663625 b'width' : int (rct .right - rct .left ),
664626 b'height' : int (rct .bottom - rct .top )
665- })
627+ })
666628 return 1
667629
668630 monitors = []
@@ -688,8 +650,8 @@ def get_pixels(self, monitor):
688650 memdc = windll .gdi32 .CreateCompatibleDC (srcdc )
689651 bmp = windll .gdi32 .CreateCompatibleBitmap (srcdc , width , height )
690652 windll .gdi32 .SelectObject (memdc , bmp )
691- windll .gdi32 .BitBlt (memdc , 0 , 0 , width , height , srcdc , left ,
692- top , SRCCOPY )
653+ windll .gdi32 .BitBlt (memdc , 0 , 0 , width , height , srcdc , left , top ,
654+ SRCCOPY )
693655 bmi = BITMAPINFO ()
694656 bmi .bmiHeader .biSize = sizeof (BITMAPINFOHEADER )
695657 bmi .bmiHeader .biWidth = width
@@ -736,8 +698,8 @@ def _arrange(self, data, width, height):
736698 off = width * (y + 1 )
737699 offset = total - off
738700 for x in range (0 , width - 2 , 3 ):
739- scanlines [off + x :off + x + 3 ] = \
740- b (data [offset + x + 2 ]), b (data [offset + x + 1 ]), b (data [offset + x ])
701+ scanlines [off + x :off + x + 3 ] = \
702+ b (data [offset + x + 2 ]), b (data [offset + x + 1 ]), b (data [offset + x ])
741703 image = b'' .join (scanlines )
742704 return image
743705
@@ -754,13 +716,9 @@ def timer(msg):
754716 start = time ()
755717 yield
756718 end = time ()
757- print ('{}: {} ms' .format (msg , (end - start )* 1000 ))
719+ print ('{}: {} ms' .format (msg , (end - start ) * 1000 ))
758720
759- systems = {
760- 'Darwin' : MSSMac ,
761- 'Linux' : MSSLinux ,
762- 'Windows' : MSSWindows
763- }
721+ systems = {'Darwin' : MSSMac , 'Linux' : MSSLinux , 'Windows' : MSSWindows }
764722 mss = systems [system ()](debug = '--debug' in argv )
765723
766724 try :
@@ -793,7 +751,8 @@ def on_exists(fname):
793751 # Screen shot of the monitor 1, with callback
794752 with timer ('Monitor 1 ' ):
795753 for fname in mss .save (output = 'mon-%d.png' ,
796- screen = 1 , callback = on_exists ):
754+ screen = 1 ,
755+ callback = on_exists ):
797756 print (' File: {}' .format (fname ))
798757 except ScreenshotError as ex :
799758 print (ex )
0 commit comments