#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK """ https://cgit.freedesktop.org/xorg/app/xrandr/tree/xrandr.c """ import ctypes X_Time = ctypes.c_ulong """ #ifndef _XTYPEDEF_XID # define _XTYPEDEF_XID # ifndef _XSERVER64 typedef unsigned long XID; # else typedef CARD32 XID; # endif #endif """ X_XID = ctypes.c_ulong """ typedef XID RROutput; """ X_RROutput = X_XID """ typedef XID RRCrtc; """ X_RRCrtc = X_XID """ /usr/include/X11/extensions/Xrandr.h typedef XID RRMode; """ X_RRMode = X_XID """ /usr/include/X11/extensions/randr.h #define RR_Connected 0 """ X_RR_Connected = 0 """ /usr/include/X11/extensions/randr.h typedef unsigned short Connection; """ X_Connection = ctypes.c_ushort """ /usr/include/X11/extensions/randr.h typedef unsigned short SubpixelOrder; """ X_SubpixelOrder = ctypes.c_ushort """ /usr/include/X11/Xdefs.h #ifndef _XTYPEDEF_ATOM # define _XTYPEDEF_ATOM # ifndef _XSERVER64 typedef unsigned long Atom; # else typedef CARD32 Atom; # endif #endif """ X_Atom = ctypes.c_ulong class X_XRRScreenResources(ctypes.Structure): """ /usr/include/X11/extensions/Xrandr.h typedef struct _XRRScreenResources { Time timestamp; Time configTimestamp; int ncrtc; RRCrtc *crtcs; int noutput; RROutput *outputs; int nmode; XRRModeInfo *modes; } XRRScreenResources; """ _fields_ = [ ('timestamp', X_Time), ('configTimestamp', X_Time), ('ncrtc', ctypes.c_int), ('crtcs', ctypes.POINTER(X_RRCrtc)), ('noutput', ctypes.c_int), ('outputs', ctypes.POINTER(X_RROutput)), ('nmode', ctypes.c_int), ('modes', ctypes.c_void_p), # ctypes.POINTER(XRRModeInfo) ] class X_XRROutputInfo(ctypes.Structure): """ typedef struct _XRROutputInfo { Time timestamp; RRCrtc crtc; char *name; int nameLen; unsigned long mm_width; unsigned long mm_height; Connection connection; SubpixelOrder subpixel_order; int ncrtc; RRCrtc *crtcs; int nclone; RROutput *clones; int nmode; int npreferred; RRMode *modes; } XRROutputInfo; """ _fields_ = [ ('timestamp', X_Time), ('crtc', X_RRCrtc), ('name', ctypes.c_char_p), ('nameLen', ctypes.c_int), ('mm_width', ctypes.c_ulong), ('mm_height', ctypes.c_ulong), ('connection', X_Connection), ('subpixel_order', X_SubpixelOrder), ('ncrtc', ctypes.c_int), ('crtcs', ctypes.POINTER(X_RRCrtc)), ('nclone', ctypes.c_int), ('clones', ctypes.POINTER(X_RROutput)), ('nmode', ctypes.c_int), ('npreferred', ctypes.c_int), ('modes', ctypes.POINTER(X_RRMode)), ] X11 = ctypes.cdll.LoadLibrary("libX11.so") Xrandr = ctypes.cdll.LoadLibrary("libXrandr.so") Xrandr.XRRGetScreenResourcesCurrent.restype = \ ctypes.POINTER(X_XRRScreenResources) """ XRROutputInfo * XRRGetOutputInfo (Display *dpy, XRRScreenResources *resources, RROutput output); """ Xrandr.XRRGetOutputInfo.restype = ctypes.POINTER(X_XRROutputInfo) Xrandr.XRRListOutputProperties.restype = ctypes.POINTER(X_Atom) class RandrScreenResources: def __init__(self, xdisplay, window): self._xdisplay = xdisplay self._p = Xrandr.XRRGetScreenResourcesCurrent(xdisplay, window) def __del__(self): X11.XFree(self._p) @property def outputs(self): return [self._p.contents.outputs[o] for o in range(self._p.contents.noutput)] def get_output_infos(self): return [RandrOutputInfo(self, o) for o in self.outputs] class RandrOutputInfo: def __init__(self, screen_resrcs: RandrScreenResources, output: X_RROutput): self._xdisplay = screen_resrcs._xdisplay self._output = output self._p = Xrandr.XRRGetOutputInfo( self._xdisplay, screen_resrcs._p, output, ) def __del__(self): X11.XFree(self._p) @property def enabled(self): return self._p.contents.crtc != 0 @property def connected(self): return self._p.contents.connection == X_RR_Connected @property def name(self): return self._p.contents.name.decode() def _get_property_atoms_list(self): nprop = ctypes.c_int() props = Xrandr.XRRListOutputProperties( self._xdisplay, self._output, ctypes.pointer(nprop), ) atoms = [props[i] for i in range(nprop.value)] X11.XFree(props) return atoms def has_backlight(self): atom = X11.XInternAtom(self._xdisplay, b'Backlight', True) if atom: props = self._get_property_atoms_list() return atom in props else: return False def process(connected, disabled, enabled, backlight, no_backlight): xdisplay = X11.XOpenDisplay(None) screen_resrcs = RandrScreenResources( xdisplay, X11.XDefaultRootWindow(xdisplay), ) found = False for output_info in screen_resrcs.get_output_infos(): if ((not connected or output_info.connected) and (not disabled or not output_info.enabled) and (not enabled or output_info.enabled) and (not backlight or output_info.has_backlight()) and (not no_backlight or not output_info.has_backlight())): found = True print(output_info.name) X11.XCloseDisplay(xdisplay) return found def _init_argparser(): import argparse argparser = argparse.ArgumentParser( description='Show names of outputs available to the X server.' + ' The exit status is 0 if an output matching the specified criteria' + ' was found, otherwise 1.', ) argparser.add_argument( '-c', '--connected', action='store_true', help='connected only', ) argparser.add_argument( '-d', '--disabled', action='store_true', help='disabled only (does not imply --connected)', ) argparser.add_argument( '-e', '--enabled', action='store_true', help='enabled only (does not imply --connected)', ) argparser.add_argument( '-b', '--backlight', action='store_true', help='outputs with backlight configurable via randr only', ) argparser.add_argument( '-B', '--no-backlight', action='store_true', help='outputs without backlight configurable via randr only', ) return argparser def main(argv): argparser = _init_argparser() try: import argcomplete except ImportError: pass args = argparser.parse_args(argv) return 0 if process(**vars(args)) else 1 if __name__ == "__main__": import sys sys.exit(main(sys.argv[1:]))