pax_global_header00006660000000000000000000000064137721642720014525gustar00rootroot0000000000000052 comment=d104660842f1b3e7d669757dde7bf5d93c6e45f9 screeninfo-0.6.7/000077500000000000000000000000001377216427200136725ustar00rootroot00000000000000screeninfo-0.6.7/.gitignore000066400000000000000000000000421377216427200156560ustar00rootroot00000000000000build/ dist/ *.egg-info/ MANIFEST screeninfo-0.6.7/LICENSE.md000066400000000000000000000051151377216427200153000ustar00rootroot00000000000000### Screeninfo The MIT License (MIT) Copyright (c) 2015 Marcin Kurczewski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --- ### DRM (Direct Rendering Manager) driver Copyright (c) 2011 The Chromium OS Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. screeninfo-0.6.7/README.md000066400000000000000000000020411377216427200151460ustar00rootroot00000000000000screeninfo ---------- Fetch location and size of physical screens. ### Supported environments - MS Windows - MS Windows: Cygwin - GNU/Linux: X11 (through Xinerama) - GNU/Linux: DRM (experimental) - OSX: (through PyOBJus) I don't plan on testing OSX or other environments myself. For this reason, I strongly encourage pull requests. ### Installation ``` pip install screeninfo ``` If you install it from sources: ``` python3 setup.py install ``` ### Usage ```python from screeninfo import get_monitors for m in get_monitors(): print(str(m)) ``` **Output**: >Monitor(x=1920, y=0, width=1920, height=1080, name=None) >Monitor(x=0, y=0, width=1920, height=1080, name=None) ### Forcing environment In some cases (emulating X server on Cygwin etc.) you might want to specify the driver directly. You can do so by passing extra parameter to `get_monitors()` like this: ```python from screeninfo import get_monitors, Enumerator for m in get_monitors(Enumerator.OSX): print(str(m)) ``` Available drivers: `windows`, `cygwin`, `x11`, `osx`. screeninfo-0.6.7/screeninfo/000077500000000000000000000000001377216427200160255ustar00rootroot00000000000000screeninfo-0.6.7/screeninfo/__init__.py000066400000000000000000000001151377216427200201330ustar00rootroot00000000000000from .common import Enumerator, Monitor from .screeninfo import get_monitors screeninfo-0.6.7/screeninfo/__main__.py000066400000000000000000000007401377216427200201200ustar00rootroot00000000000000import argparse from screeninfo.common import Enumerator from .screeninfo import get_monitors def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument( "enumerator", nargs="?", choices=[item.value for item in Enumerator] ) return parser.parse_args() def main() -> None: args = parse_args() for monitor in get_monitors(args.enumerator): print(str(monitor)) if __name__ == "__main__": main() screeninfo-0.6.7/screeninfo/common.py000066400000000000000000000014621377216427200176720ustar00rootroot00000000000000import enum import typing as T from dataclasses import dataclass @dataclass class Monitor: """Stores the resolution and position of a monitor.""" x: int y: int width: int height: int width_mm: T.Optional[int] = None height_mm: T.Optional[int] = None name: T.Optional[str] = None def __repr__(self) -> str: return ( f"Monitor(" f"x={self.x}, y={self.y}, " f"width={self.width}, height={self.height}, " f"width_mm={self.width_mm}, height_mm={self.height_mm}, " f"name={self.name!r}" f")" ) class ScreenInfoError(Exception): pass class Enumerator(enum.Enum): Windows = "windows" Cygwin = "cygwin" Xrandr = "xrandr" Xinerama = "xinerama" DRM = "drm" OSX = "osx" screeninfo-0.6.7/screeninfo/enumerators/000077500000000000000000000000001377216427200203715ustar00rootroot00000000000000screeninfo-0.6.7/screeninfo/enumerators/__init__.py000066400000000000000000000003331377216427200225010ustar00rootroot00000000000000import screeninfo.enumerators.cygwin import screeninfo.enumerators.osx import screeninfo.enumerators.windows import screeninfo.enumerators.xinerama import screeninfo.enumerators.xrandr import screeninfo.enumerators.drm screeninfo-0.6.7/screeninfo/enumerators/cygwin.py000066400000000000000000000027551377216427200222540ustar00rootroot00000000000000import typing as T from screeninfo.common import Monitor def enumerate_monitors() -> T.Iterable[Monitor]: import ctypes LONG = ctypes.c_int32 BOOL = ctypes.c_int HANDLE = ctypes.c_void_p HMONITOR = HANDLE HDC = HANDLE class RECT(ctypes.Structure): _fields_ = [ ("left", LONG), ("top", LONG), ("right", LONG), ("bottom", LONG), ] user32 = ctypes.cdll.LoadLibrary("user32.dll") ptr_size = ctypes.sizeof(ctypes.c_void_p) if ptr_size == ctypes.sizeof(ctypes.c_long): WPARAM = ctypes.c_ulong LPARAM = ctypes.c_long elif ptr_size == ctypes.sizeof(ctypes.c_longlong): WPARAM = ctypes.c_ulonglong LPARAM = ctypes.c_longlong MonitorEnumProc = ctypes.CFUNCTYPE( BOOL, HMONITOR, HDC, ctypes.POINTER(RECT), LPARAM ) user32.EnumDisplayMonitors.argtypes = [ HANDLE, ctypes.POINTER(RECT), MonitorEnumProc, LPARAM, ] user32.EnumDisplayMonitors.restype = ctypes.c_bool monitors = [] def callback(monitor: T.Any, dc: T.Any, rect: T.Any, data: T.Any) -> int: rct = rect.contents monitors.append( Monitor( x=rct.left, y=rct.top, width=rct.right - rct.left, height=rct.bottom - rct.top, ) ) return 1 user32.EnumDisplayMonitors(None, None, MonitorEnumProc(callback), 0) yield from monitors screeninfo-0.6.7/screeninfo/enumerators/drm.py000066400000000000000000000241231377216427200215270ustar00rootroot00000000000000import os import typing as T from screeninfo.common import Monitor, ScreenInfoError def enumerate_monitors() -> T.Iterable[Monitor]: import ctypes import ctypes.util from screeninfo.util import load_library libdrm = load_library("drm") class DrmBase(ctypes.Structure): fd = None needs_free = False class DrmModeRes(DrmBase): _fields_ = [ ("count_fbs", ctypes.c_int), ("_fbs", ctypes.POINTER(ctypes.c_uint32)), ("count_crtcs", ctypes.c_int), ("_crtcs", ctypes.POINTER(ctypes.c_uint32)), ("count_connectors", ctypes.c_int), ("_connectors", ctypes.POINTER(ctypes.c_uint32)), ("count_encoders", ctypes.c_int), ("_encoders", ctypes.POINTER(ctypes.c_uint32)), ("min_width", ctypes.c_uint32), ("max_width", ctypes.c_uint32), ("min_height", ctypes.c_uint32), ("max_height", ctypes.c_uint32), ] def __del__(self) -> None: if self.needs_free: libdrm.drmModeFreeResources(ctypes.byref(self)) @property def crtcs(self) -> T.List["DrmModeCrtc"]: ret = [] for i in range(self.count_crtcs): crtc = libdrm.drmModeGetCrtc(self.fd, self._crtcs[i]).contents crtc.fd = self.fd crtc.need_free = True ret.append(crtc) return ret @property def connectors(self) -> T.List["DrmModeConnector"]: ret = [] for i in range(self.count_connectors): pconn = libdrm.drmModeGetConnector( self.fd, self._connectors[i] ) if not pconn: continue conn = pconn.contents conn.fd = self.fd conn.need_free = True ret.append(conn) return ret class DrmModeModeInfo(DrmBase): DRM_DISPLAY_MODE_LEN = 32 _fields_ = [ ("clock", ctypes.c_uint32), ("hdisplay", ctypes.c_uint16), ("hsync_start", ctypes.c_uint16), ("hsync_end", ctypes.c_uint16), ("htotal", ctypes.c_uint16), ("hskew", ctypes.c_uint16), ("vdisplay", ctypes.c_uint16), ("vsync_start", ctypes.c_uint16), ("vsync_end", ctypes.c_uint16), ("vtotal", ctypes.c_uint16), ("vscan", ctypes.c_uint16), ("vrefresh", ctypes.c_uint32), ("flags", ctypes.c_uint32), ("type", ctypes.c_uint32), ("name", ctypes.c_char * DRM_DISPLAY_MODE_LEN), ] def __del__(self) -> None: if self.needs_free: libdrm.drmModeFreeModeInfo(ctypes.byref(self)) class DrmModeConnector(DrmBase): DRM_MODE_CONNECTED = 1 DRM_MODE_DISCONNECTED = 2 DRM_MODE_UNKNOWNCONNECTION = 3 DRM_MODE_SUBPIXEL_UNKNOWN = 1 DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2 DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3 DRM_MODE_SUBPIXEL_VERTICAL_RGB = 4 DRM_MODE_SUBPIXEL_VERTICAL_BGR = 5 DRM_MODE_SUBPIXEL_NONE = 6 DRM_MODE_CONNECTOR_Unknown = 0 DRM_MODE_CONNECTOR_VGA = 1 DRM_MODE_CONNECTOR_DVII = 2 DRM_MODE_CONNECTOR_DVID = 3 DRM_MODE_CONNECTOR_DVIA = 4 DRM_MODE_CONNECTOR_Composite = 5 DRM_MODE_CONNECTOR_SVIDEO = 6 DRM_MODE_CONNECTOR_LVDS = 7 DRM_MODE_CONNECTOR_Component = 8 DRM_MODE_CONNECTOR_9PinDIN = 9 DRM_MODE_CONNECTOR_DisplayPort = 10 DRM_MODE_CONNECTOR_HDMIA = 11 DRM_MODE_CONNECTOR_HDMIB = 12 DRM_MODE_CONNECTOR_TV = 13 DRM_MODE_CONNECTOR_eDP = 14 DRM_MODE_CONNECTOR_VIRTUAL = 15 DRM_MODE_CONNECTOR_DSI = 16 DRM_MODE_CONNECTOR_DPI = 17 DRM_MODE_CONNECTOR_WRITEBACK = 18 DRM_MODE_CONNECTOR_SPI = 19 _fields_ = [ ("connector_id", ctypes.c_uint32), ("encoder_id", ctypes.c_uint32), ("connector_type", ctypes.c_uint32), ("connector_type_id", ctypes.c_uint32), ("connection", ctypes.c_uint), ("mmWidth", ctypes.c_uint32), ("mmHeight", ctypes.c_uint32), ("subpixel", ctypes.c_uint), ("count_modes", ctypes.c_int), ("modes", ctypes.POINTER(DrmModeModeInfo)), ("count_props", ctypes.c_int), ("props", ctypes.POINTER(ctypes.c_uint32)), ("prop_values", ctypes.POINTER(ctypes.c_uint64)), ("count_encoders", ctypes.c_int), ("encoders", ctypes.POINTER(ctypes.c_uint32)), ] def __del__(self) -> None: if self.needs_free: libdrm.drmModeFreeConnector(ctypes.byref(self)) @property def encoder(self) -> T.Optional["DrmModeEncoder"]: encoder_ptr = libdrm.drmModeGetEncoder(self.fd, self.encoder_id) if encoder_ptr: encoder = encoder_ptr.contents encoder.fd = self.fd encoder.need_free = True return T.cast("DrmModeEncoder", encoder) return None class DrmModeEncoder(DrmBase): _fields_ = [ ("encoder_id", ctypes.c_uint32), ("encoder_type", ctypes.c_uint32), ("crtc_id", ctypes.c_uint32), ("possible_crtcs", ctypes.c_uint32), ("possible_clones", ctypes.c_uint32), ] def __del__(self) -> None: if self.need_free: libdrm.drmModeFreeEncoder(ctypes.byref(self)) @property def crtc(self) -> "DrmModeCrtc": crtc = libdrm.drmModeGetCrtc(self.fd, self.crtc_id).contents crtc.fd = self.fd crtc.need_free = True return T.cast("DrmModeCrtc", crtc) class DrmModeCrtc(DrmBase): _fields_ = [ ("crtc_id", ctypes.c_uint32), ("buffer_id", ctypes.c_uint32), ("x", ctypes.c_uint32), ("y", ctypes.c_uint32), ("width", ctypes.c_uint32), ("height", ctypes.c_uint32), ("mode_valid", ctypes.c_int), ("mode", DrmModeModeInfo), ("gamma_size", ctypes.c_int), ] def __del__(self) -> None: if self.need_free: libdrm.drmModeFreeCrtc(ctypes.byref(self)) libdrm.drmModeGetResources.argtypes = [ctypes.c_int] libdrm.drmModeGetResources.restype = ctypes.POINTER(DrmModeRes) libdrm.drmModeFreeResources.argtypes = [ctypes.POINTER(DrmModeRes)] libdrm.drmModeFreeResources.restype = None libdrm.drmModeGetConnector.argtypes = [ctypes.c_int, ctypes.c_uint32] libdrm.drmModeGetConnector.restype = ctypes.POINTER(DrmModeConnector) libdrm.drmModeFreeConnector.argtypes = [ctypes.POINTER(DrmModeConnector)] libdrm.drmModeFreeConnector.restype = None libdrm.drmModeGetEncoder.argtypes = [ctypes.c_int, ctypes.c_uint32] libdrm.drmModeGetEncoder.restype = ctypes.POINTER(DrmModeEncoder) libdrm.drmModeFreeEncoder.argtypes = [ctypes.POINTER(DrmModeEncoder)] libdrm.drmModeFreeEncoder.restype = None libdrm.drmModeGetCrtc.argtypes = [ctypes.c_int, ctypes.c_uint32] libdrm.drmModeGetCrtc.restype = ctypes.POINTER(DrmModeCrtc) libdrm.drmModeFreeCrtc.argtypes = [ctypes.POINTER(DrmModeCrtc)] libdrm.drmModeFreeCrtc.restype = None DRM_MAX_MINOR = 16 DRM_DIR_NAME = "/dev/dri" DRM_DEV_NAME = "%s/card%d" def get_connector_name(connector: "DrmModeConnector") -> str: try: prefix = { DrmModeConnector.DRM_MODE_CONNECTOR_Unknown: "Unknown", DrmModeConnector.DRM_MODE_CONNECTOR_VGA: "VGA", DrmModeConnector.DRM_MODE_CONNECTOR_DVII: "DVI-I", DrmModeConnector.DRM_MODE_CONNECTOR_DVID: "DVI-D", DrmModeConnector.DRM_MODE_CONNECTOR_DVIA: "DVI-A", DrmModeConnector.DRM_MODE_CONNECTOR_Composite: "Composite", DrmModeConnector.DRM_MODE_CONNECTOR_SVIDEO: "SVIDEO", DrmModeConnector.DRM_MODE_CONNECTOR_LVDS: "LVDS", DrmModeConnector.DRM_MODE_CONNECTOR_Component: "Component", DrmModeConnector.DRM_MODE_CONNECTOR_9PinDIN: "DIN", DrmModeConnector.DRM_MODE_CONNECTOR_DisplayPort: "DP", DrmModeConnector.DRM_MODE_CONNECTOR_HDMIA: "HDMI-A", DrmModeConnector.DRM_MODE_CONNECTOR_HDMIB: "HDMI-B", DrmModeConnector.DRM_MODE_CONNECTOR_TV: "TV", DrmModeConnector.DRM_MODE_CONNECTOR_eDP: "eDP", DrmModeConnector.DRM_MODE_CONNECTOR_VIRTUAL: "Virtual", DrmModeConnector.DRM_MODE_CONNECTOR_DSI: "DSI", DrmModeConnector.DRM_MODE_CONNECTOR_DPI: "DPI", DrmModeConnector.DRM_MODE_CONNECTOR_WRITEBACK: "Writeback", DrmModeConnector.DRM_MODE_CONNECTOR_SPI: "SPI", }[connector.connector_type] except KeyError: prefix = "Unknown" return f"{prefix}-{connector.connector_type_id}" for card_no in range(DRM_MAX_MINOR): card_path = DRM_DEV_NAME % (DRM_DIR_NAME, card_no) try: fd = os.open(card_path, os.O_RDONLY) except OSError: continue if fd < 0: continue try: res = libdrm.drmModeGetResources(fd) if not res: raise ScreenInfoError("Failed to get drm resources") res = res.contents res.fd = fd res.needs_free = True for connector in res.connectors: if connector.connection == DrmModeConnector.DRM_MODE_CONNECTED: crtc = connector.encoder.crtc yield Monitor( x=crtc.x, y=crtc.y, width=crtc.width, height=crtc.height, width_mm=connector.mmWidth, height_mm=connector.mmHeight, name=get_connector_name(connector), ) finally: os.close(fd) screeninfo-0.6.7/screeninfo/enumerators/osx.py000066400000000000000000000006741377216427200215630ustar00rootroot00000000000000import typing as T from screeninfo.common import Monitor def enumerate_monitors() -> T.Iterable[Monitor]: from AppKit import NSScreen screens = NSScreen.screens() for screen in screens: f = screen.frame if callable(f): f = f() yield Monitor( x=int(f.origin.x), y=int(f.origin.y), width=int(f.size.width), height=int(f.size.height), ) screeninfo-0.6.7/screeninfo/enumerators/windows.py000066400000000000000000000056621377216427200224460ustar00rootroot00000000000000import typing as T from screeninfo.common import Monitor def enumerate_monitors() -> T.Iterable[Monitor]: import ctypes import ctypes.wintypes CCHDEVICENAME = 32 # gdi32.GetDeviceCaps keys for monitor size in mm HORZSIZE = 4 VERTSIZE = 6 MonitorEnumProc = ctypes.WINFUNCTYPE( ctypes.c_int, ctypes.c_ulong, ctypes.c_ulong, ctypes.POINTER(ctypes.wintypes.RECT), ctypes.c_double, ) class MONITORINFOEXW(ctypes.Structure): _fields_ = [ ("cbSize", ctypes.wintypes.DWORD), ("rcMonitor", ctypes.wintypes.RECT), ("rcWork", ctypes.wintypes.RECT), ("dwFlags", ctypes.wintypes.DWORD), ("szDevice", ctypes.wintypes.WCHAR * CCHDEVICENAME), ] monitors = [] def callback(monitor: T.Any, dc: T.Any, rect: T.Any, data: T.Any) -> int: info = MONITORINFOEXW() info.cbSize = ctypes.sizeof(MONITORINFOEXW) if ctypes.windll.user32.GetMonitorInfoW(monitor, ctypes.byref(info)): name = info.szDevice else: name = None h_size = ctypes.windll.gdi32.GetDeviceCaps(dc, HORZSIZE) v_size = ctypes.windll.gdi32.GetDeviceCaps(dc, VERTSIZE) rct = rect.contents monitors.append( Monitor( x=rct.left, y=rct.top, width=rct.right - rct.left, height=rct.bottom - rct.top, width_mm=h_size, height_mm=v_size, name=name, ) ) return 1 # Make the process DPI aware so it will detect the actual # resolution and not a virtualized resolution reported by # Windows when DPI virtualization is in use. # # benshep 2020-03-31: this gives the correct behaviour on Windows 10 when # multiple monitors have different DPIs. ctypes.windll.shcore.SetProcessDpiAwareness(2) # On Python 3.8.X GetDC randomly fails returning an invalid DC. # To workaround this request a number of DCs until a valid DC is returned. for retry in range(100): # Create a Device Context for the full virtual desktop. dc_full = ctypes.windll.user32.GetDC(None) if dc_full > 0: # Got a valid DC, break. break ctypes.windll.user32.ReleaseDC(dc_full) else: # Fallback to device context 0 that is the whole # desktop. This allows fetching resolutions # but monitor specific device contexts are not # passed to the callback which means that physical # sizes can't be read. dc_full = 0 # Call EnumDisplayMonitors with the non-NULL DC # so that non-NULL DCs are passed onto the callback. # We want monitor specific DCs in the callback. ctypes.windll.user32.EnumDisplayMonitors( dc_full, None, MonitorEnumProc(callback), 0 ) ctypes.windll.user32.ReleaseDC(dc_full) yield from monitors screeninfo-0.6.7/screeninfo/enumerators/xinerama.py000066400000000000000000000030551377216427200225520ustar00rootroot00000000000000import typing as T from screeninfo.common import Monitor, ScreenInfoError def enumerate_monitors() -> T.Iterable[Monitor]: import ctypes from screeninfo.util import load_library class XineramaScreenInfo(ctypes.Structure): _fields_ = [ ("screen_number", ctypes.c_int), ("x", ctypes.c_short), ("y", ctypes.c_short), ("width", ctypes.c_short), ("height", ctypes.c_short), ] xlib = load_library("X11") xlib.XOpenDisplay.argtypes = [ctypes.c_char_p] xlib.XOpenDisplay.restype = ctypes.POINTER(ctypes.c_void_p) xlib.XFree.argtypes = [ctypes.c_void_p] xlib.XFree.restype = None xinerama = load_library("Xinerama") display = xlib.XOpenDisplay(b"") if not display: raise ScreenInfoError("Could not open display") try: if not xinerama.XineramaIsActive(display): raise ScreenInfoError("Xinerama is not active") number = ctypes.c_int() xinerama.XineramaQueryScreens.restype = ctypes.POINTER( XineramaScreenInfo ) infos = xinerama.XineramaQueryScreens(display, ctypes.byref(number)) infos = ctypes.cast( infos, ctypes.POINTER(XineramaScreenInfo * number.value) ).contents for info in infos: yield Monitor( x=info.x, y=info.y, width=info.width, height=info.height ) xlib.XFree(infos) finally: xlib.XCloseDisplay.restype = ctypes.POINTER(ctypes.c_void_p) xlib.XCloseDisplay(display) screeninfo-0.6.7/screeninfo/enumerators/xrandr.py000066400000000000000000000077431377216427200222540ustar00rootroot00000000000000import sys import typing as T from screeninfo.common import Monitor, ScreenInfoError def enumerate_monitors() -> T.Iterable[Monitor]: import ctypes import ctypes.util from screeninfo.util import load_library RR_Connected = 0 class XRRCrtcInfo(ctypes.Structure): _fields_ = [ ("timestamp", ctypes.c_ulong), ("x", ctypes.c_int), ("y", ctypes.c_int), ("width", ctypes.c_int), ("height", ctypes.c_int), ("mode", ctypes.c_long), ("rotation", ctypes.c_int), ("noutput", ctypes.c_int), ("outputs", ctypes.POINTER(ctypes.c_long)), ("rotations", ctypes.c_ushort), ("npossible", ctypes.c_int), ("possible", ctypes.POINTER(ctypes.c_long)), ] class XRRScreenResources(ctypes.Structure): _fields_ = [ ("timestamp", ctypes.c_ulong), ("configTimestamp", ctypes.c_ulong), ("ncrtc", ctypes.c_int), ("crtcs", ctypes.POINTER(ctypes.c_ulong)), ("noutput", ctypes.c_int), ("outputs", ctypes.POINTER(ctypes.c_ulong)), ("nmode", ctypes.c_int), ("modes", ctypes.c_void_p), # ctypes.POINTER(XRRModeInfo) ] class XRROutputInfo(ctypes.Structure): _fields_ = [ ("timestamp", ctypes.c_ulong), ("crtc", ctypes.c_ulong), ("name", ctypes.c_char_p), ("nameLen", ctypes.c_int), ("mm_width", ctypes.c_ulong), ("mm_height", ctypes.c_ulong), ("connection", ctypes.c_ushort), ("subpixel_order", ctypes.c_ushort), ("ncrtc", ctypes.c_int), ("crtcs", ctypes.POINTER(ctypes.c_ulong)), ("nclone", ctypes.c_int), ("clones", ctypes.POINTER(ctypes.c_ulong)), ("nmode", ctypes.c_int), ("npreferred", ctypes.c_int), ("modes", ctypes.POINTER(ctypes.c_ulong)), ] xlib = load_library("X11") xlib.XOpenDisplay.argtypes = [ctypes.c_char_p] xlib.XOpenDisplay.restype = ctypes.POINTER(ctypes.c_void_p) xrandr = load_library("Xrandr") xrandr.XRRGetScreenResourcesCurrent.restype = ctypes.POINTER( XRRScreenResources ) xrandr.XRRGetOutputInfo.restype = ctypes.POINTER(XRROutputInfo) xrandr.XRRGetCrtcInfo.restype = ctypes.POINTER(XRRCrtcInfo) display = xlib.XOpenDisplay(b"") if not display: raise ScreenInfoError("Could not open display") try: root_window = xlib.XDefaultRootWindow(display) screen_resources = xrandr.XRRGetScreenResourcesCurrent( display, root_window ) for i in range(screen_resources.contents.noutput): output_info = xrandr.XRRGetOutputInfo( display, screen_resources, screen_resources.contents.outputs[i] ) if output_info.contents.connection != RR_Connected: continue if not output_info.contents.crtc: continue try: crtc_info = xrandr.XRRGetCrtcInfo( display, ctypes.byref(output_info), output_info.contents.crtc, ) try: yield Monitor( x=crtc_info.contents.x, y=crtc_info.contents.y, width=crtc_info.contents.width, height=crtc_info.contents.height, width_mm=output_info.contents.mm_width, height_mm=output_info.contents.mm_height, name=output_info.contents.name.decode( sys.getfilesystemencoding() ), ) finally: xrandr.XRRFreeCrtcInfo(crtc_info) finally: xrandr.XRRFreeOutputInfo(output_info) finally: xlib.XCloseDisplay(display) screeninfo-0.6.7/screeninfo/screeninfo.py000066400000000000000000000020501377216427200205270ustar00rootroot00000000000000import importlib import typing as T from pathlib import Path from screeninfo import enumerators from screeninfo.common import Enumerator, Monitor, ScreenInfoError ENUMERATOR_MAP = { Enumerator.Windows: enumerators.windows, Enumerator.Cygwin: enumerators.cygwin, Enumerator.Xrandr: enumerators.xrandr, Enumerator.Xinerama: enumerators.xinerama, Enumerator.DRM: enumerators.drm, Enumerator.OSX: enumerators.osx, } def _get_monitors(enumerator: Enumerator) -> T.List[Monitor]: return list(ENUMERATOR_MAP[enumerator].enumerate_monitors()) def get_monitors( name: T.Union[Enumerator, str, None] = None ) -> T.List[Monitor]: """Returns a list of :class:`Monitor` objects based on active monitors.""" enumerator = Enumerator(name) if name is not None else None if enumerator is not None: return _get_monitors(enumerator) for enumerator in Enumerator: try: return _get_monitors(enumerator) except Exception: pass raise ScreenInfoError("No enumerators available") screeninfo-0.6.7/screeninfo/util.py000066400000000000000000000004311377216427200173520ustar00rootroot00000000000000import ctypes.util import typing as T from screeninfo.common import ScreenInfoError def load_library(name: str) -> T.Any: path = ctypes.util.find_library(name) if not path: raise ScreenInfoError("Could not load " + name) return ctypes.cdll.LoadLibrary(path) screeninfo-0.6.7/setup.cfg000066400000000000000000000000501377216427200155060ustar00rootroot00000000000000[metadata] description-file = README.md screeninfo-0.6.7/setup.py000066400000000000000000000016011377216427200154020ustar00rootroot00000000000000from setuptools import setup # Collect the Current README docs for PyPI with open("README.md", "r") as fh: long_description = fh.read() setup( name="screeninfo", packages=["screeninfo", "screeninfo.enumerators"], version="0.6.7", description="Fetch location and size of physical screens.", long_description=long_description, long_description_content_type="text/markdown", author="rr-", author_email="rr-@sakuya.pl", url="https://github.com/rr-/screeninfo", keywords=["screen", "monitor", "desktop"], classifiers=[ 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', ], install_requires=[ "dataclasses ; python_version<'3.7'", 'Cython ; sys_platform=="darwin"', 'pyobjc-framework-Cocoa ; sys_platform=="darwin"', ], )