Esempio n. 1
0
    def grab(self, monitor):
        # type: (Dict[str, int]) -> ScreenShot
        """ Retrieve all pixels from a monitor. Pixels have to be RGB. """

        # Convert PIL bbox style
        if isinstance(monitor, tuple):
            monitor = {
                'left': monitor[0],
                'top': monitor[1],
                'width': monitor[2] - monitor[0],
                'height': monitor[3] - monitor[1],
            }

        ximage = self.xlib.XGetImage(self.display, self.drawable,
                                     monitor['left'], monitor['top'],
                                     monitor['width'], monitor['height'],
                                     PLAINMASK, ZPIXMAP)
        if not ximage:
            raise ScreenShotError('xlib.XGetImage() failed.', locals())

        bits_per_pixel = ximage.contents.bits_per_pixel
        if bits_per_pixel != 32:
            raise ScreenShotError(('[XImage] bits per pixel value '
                                   'not (yet?) implemented.'), locals())

        data = ctypes.cast(
            ximage.contents.data,
            ctypes.POINTER(ctypes.c_ubyte * monitor['height'] *
                           monitor['width'] * 4))
        data = bytearray(data.contents)

        # Free
        self.xlib.XDestroyImage(ximage)

        return self.cls_image(data, monitor)
Esempio n. 2
0
    def grab(self, monitor):
        # type: (Dict[str, int]) -> ScreenShot
        """
        See :meth:`MSSBase.grab <mss.base.MSSBase.grab>` for full details.
        """

        # Convert PIL bbox style
        if isinstance(monitor, tuple):
            monitor = {
                'left': monitor[0],
                'top': monitor[1],
                'width': monitor[2] - monitor[0],
                'height': monitor[3] - monitor[1],
            }

        core = self.core
        rect = CGRect((monitor['left'], monitor['top']),
                      (monitor['width'], monitor['height']))

        image_ref = core.CGWindowListCreateImage(rect, 1, 0, 0)
        if not image_ref:
            raise ScreenShotError(
                'CoreGraphics.CGWindowListCreateImage() failed.', locals())

        width = int(core.CGImageGetWidth(image_ref))
        height = int(core.CGImageGetHeight(image_ref))
        prov = copy_data = None
        try:
            prov = core.CGImageGetDataProvider(image_ref)
            copy_data = core.CGDataProviderCopyData(prov)
            data_ref = core.CFDataGetBytePtr(copy_data)
            buf_len = core.CFDataGetLength(copy_data)
            raw = ctypes.cast(
                data_ref, ctypes.POINTER(ctypes.c_ubyte * buf_len))
            data = bytearray(raw.contents)

            # Remove padding per row
            bytes_per_row = int(core.CGImageGetBytesPerRow(image_ref))
            bytes_per_pixel = int(core.CGImageGetBitsPerPixel(image_ref))
            bytes_per_pixel = (bytes_per_pixel + 7) // 8

            if bytes_per_pixel * width != bytes_per_row:
                cropped = bytearray()
                for row in range(height):
                    start = row * bytes_per_row
                    end = start + width * bytes_per_pixel
                    cropped.extend(data[start:end])
                data = cropped
        finally:
            if prov:
                core.CGDataProviderRelease(prov)
            if copy_data:
                core.CFRelease(copy_data)

        return self.cls_image(data, monitor, size=Size(width, height))
Esempio n. 3
0
    def __init__(self):
        # type: () -> None
        """ macOS initialisations. """

        coregraphics = ctypes.util.find_library('CoreGraphics')
        if not coregraphics:
            raise ScreenShotError('No CoreGraphics library found.', locals())
        self.core = ctypes.cdll.LoadLibrary(coregraphics)

        self._set_argtypes()
        self._set_restypes()
Esempio n. 4
0
    def __init__(self, display=None):
        # type: (bytes) -> None
        """ GNU/Linux initialisations. """

        if not display:
            try:
                display = os.environ['DISPLAY'].encode('utf-8')
            except KeyError:
                raise ScreenShotError('$DISPLAY not set.', locals())

        if not isinstance(display, bytes):
            display = display.encode('utf-8')

        if b':' not in display:
            raise ScreenShotError('Bad display value.', locals())

        x11 = ctypes.util.find_library('X11')
        if not x11:
            raise ScreenShotError('No X11 library found.', locals())
        self.xlib = ctypes.cdll.LoadLibrary(x11)

        xrandr = ctypes.util.find_library('Xrandr')
        if not xrandr:
            raise ScreenShotError('No Xrandr extension found.', locals())
        self.xrandr = ctypes.cdll.LoadLibrary(xrandr)

        self._set_argtypes()
        self._set_restypes()

        self.display = self.xlib.XOpenDisplay(display)
        try:
            self.display.contents
        except ValueError:
            raise ScreenShotError('Cannot open display.', locals())

        self.root = self.xlib.XDefaultRootWindow(
            self.display, self.xlib.XDefaultScreen(self.display))

        # Fix for XRRGetScreenResources and XGetImage:
        #     expected LP_Display instance instead of LP_XWindowAttributes
        self.drawable = ctypes.cast(self.root, ctypes.POINTER(Display))
Esempio n. 5
0
        def validate(value, _, args):
            # type: (int, Any, Tuple[Any, Any]) -> None
            """ Validate the returned value of xrandr.XRRGetScreenResources().
                We can end on a segfault if not:
                    Xlib:  extension "RANDR" missing on display "...".
            """

            if value == 0:
                raise ScreenShotError(('xrandr.XRRGetScreenResources() failed.'
                                       ' NULL pointer received.'), locals())

            return args
Esempio n. 6
0
    def pixel(self, coord_x, coord_y):
        # type: (int, int) -> Tuple[int, int, int]
        """
        Returns the pixel value at a given position.

        :param int coord_x: The x coordinate.
        :param int coord_y: The y coordinate.
        :return tuple: The pixel value as (R, G, B).
        """

        try:
            return self.pixels[coord_y][coord_x]
        except IndexError:
            raise ScreenShotError('Pixel location out of range.', locals())
Esempio n. 7
0
def mss(**kwargs):
    # type: (**str) -> MSS
    """ Factory returning a proper MSS class instance.

        It detects the plateform we are running on
        and choose the most adapted mss_class to take
        screenshots.

        It then proxies its arguments to the class for
        instantiation.
    """

    operating_system = platform.system().lower()
    if operating_system == 'darwin':
        from .darwin import MSS
    elif operating_system == 'linux':
        from .linux import MSS
    elif operating_system == 'windows':
        from .windows import MSS
    else:
        raise ScreenShotError('System not (yet?) implemented.', locals())

    return MSS(**kwargs)
Esempio n. 8
0
    def save(self, mon=0, output='monitor-{mon}.png', callback=None):
        # type: (int, str, Callable[[str], None]) -> Iterator[str]
        """
        Grab a screen shot and save it to a file.

        :param int mon: The monitor to screen shot (default=0).
                        -1: grab one screen shot of all monitors
                         0: grab one screen shot by monitor
                        N: grab the screen shot of the monitor N

        :param str output: The output filename.

            It can take several keywords to customize the filename:
            - `{mon}`: the monitor number
            - `{top}`: the screen shot y-coordinate of the upper-left corner
            - `{left}`: the screen shot x-coordinate of the upper-left corner
            - `{width}`: the screen shot's width
            - `{height}`: the screen shot's height
            - `{date}`: the current date using the default formatter

            As it is using the `format()` function, you can specify
            formatting options like `{date:%Y-%m-%s}`.

        :param callable callback: Callback called before saving the
            screen shot to a file.  Take the `output` argument as parameter.

        :return generator: Created file(s).
        """

        monitors = self.monitors
        if not monitors:
            raise ScreenShotError('No monitor found.')

        if mon == 0:
            # One screen shot by monitor
            for idx, monitor in enumerate(monitors[1:], 1):
                fname = output.format(mon=idx, date=datetime.now(), **monitor)
                if callable(callback):
                    callback(fname)
                sct = self.grab(monitor)
                to_png(sct.rgb,
                       sct.size,
                       level=self.compression_level,
                       output=fname)
                yield fname
        else:
            # A screen shot of all monitors together or
            # a screen shot of the monitor N.
            mon = 0 if mon == -1 else mon
            try:
                monitor = monitors[mon]
            except IndexError:
                raise ScreenShotError('Monitor does not exist.', locals())

            output = output.format(mon=mon, date=datetime.now(), **monitor)
            if callable(callback):
                callback(output)
            sct = self.grab(monitor)
            to_png(sct.rgb,
                   sct.size,
                   level=self.compression_level,
                   output=output)
            yield output
Esempio n. 9
0
    def grab(self, monitor):
        # type: (Dict[str, int]) -> ScreenShot
        """ Retrieve all pixels from a monitor. Pixels have to be RGB.

            In the code, there are few interesting things:

            [1] bmi.bmiHeader.biHeight = -height

            A bottom-up DIB is specified by setting the height to a
            positive number, while a top-down DIB is specified by
            setting the height to a negative number.
            https://msdn.microsoft.com/en-us/library/ms787796.aspx
            https://msdn.microsoft.com/en-us/library/dd144879%28v=vs.85%29.aspx


            [2] bmi.bmiHeader.biBitCount = 32
                image_data = create_string_buffer(height * width * 4)

            We grab the image in RGBX mode, so that each word is 32bit
            and we have no striding, then we transform to RGB.
            Inspired by https://github.com/zoofIO/flexx


            [3] bmi.bmiHeader.biClrUsed = 0
                bmi.bmiHeader.biClrImportant = 0

            When biClrUsed and biClrImportant are set to zero, there
            is "no" color table, so we can read the pixels of the bitmap
            retrieved by gdi32.GetDIBits() as a sequence of RGB values.
            Thanks to http://stackoverflow.com/a/3688682
        """

        # Convert PIL bbox style
        if isinstance(monitor, tuple):
            monitor = {
                'left': monitor[0],
                'top': monitor[1],
                'width': monitor[2] - monitor[0],
                'height': monitor[3] - monitor[1],
            }

        gdi = ctypes.windll.gdi32
        width, height = monitor['width'], monitor['height']

        if (self._bbox['height'], self._bbox['width']) != (height, width):
            self._bbox = monitor
            self._bmi.bmiHeader.biWidth = width
            self._bmi.bmiHeader.biHeight = -height  # Why minus? [1]
            self._data = ctypes.create_string_buffer(width * height * 4)  # [2]
            self._bmp = gdi.CreateCompatibleBitmap(self._srcdc, width, height)
            gdi.SelectObject(self._memdc, self._bmp)

        gdi.BitBlt(self._memdc, 0, 0, width, height, self._srcdc,
                   monitor['left'], monitor['top'], SRCCOPY | CAPTUREBLT)
        bits = gdi.GetDIBits(self._memdc, self._bmp, 0, height, self._data,
                             self._bmi, DIB_RGB_COLORS)
        if bits != height:
            del self._data
            raise ScreenShotError('gdi32.GetDIBits() failed.', locals())

        return self.cls_image(self._data, monitor)