diff --git a/.gitignore b/.gitignore index d8efd36..90c7ea2 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ nosetests.xml coverage.xml *,cover .hypothesis/ +Coverage_report/ +profilex_output # Translations *.mo diff --git a/Xlib/XK.py b/Xlib/XK.py index 7603ccd..6b3c6ec 100644 --- a/Xlib/XK.py +++ b/Xlib/XK.py @@ -25,7 +25,13 @@ from Xlib.X import NoSymbol +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + def string_to_keysym(keysym): + # type: (str) -> int '''Return the (16 bit) numeric code of keysym. Given the name of a keysym as a string, return its numeric code. @@ -34,6 +40,7 @@ def string_to_keysym(keysym): return globals().get('XK_' + keysym, NoSymbol) def load_keysym_group(group): + # type: (str) -> None '''Load all the keysyms in group. Given a group name such as 'latin1' or 'katakana' load the keysyms @@ -64,10 +71,15 @@ def _load_keysyms_into_XK(mod): # Always import miscellany and latin1 keysyms load_keysym_group('miscellany') +if TYPE_CHECKING: + from Xlib.keysymdef.miscellany import * load_keysym_group('latin1') +if TYPE_CHECKING: + from Xlib.keysymdef.latin1 import * def keysym_to_string(keysym): + # type: (int) -> str | None '''Translate a keysym (16 bit number) into a python string. This will pass 0 to 0xff as well as XK_BackSpace, XK_Tab, XK_Clear, diff --git a/Xlib/display.py b/Xlib/display.py index 87b9aa6..4b09eff 100644 --- a/Xlib/display.py +++ b/Xlib/display.py @@ -41,6 +41,28 @@ from .xobject import colormap from .xobject import cursor +try: + from typing import TYPE_CHECKING, overload, TypeVar, Optional, Union, Any +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Sequence, Callable + from typing_extensions import Literal + from re import Pattern + _T = TypeVar("_T") + _ErrorHandler = Callable[[error.XError, Optional[rq.Request]], _T] + _ResourceBaseClass = Union[ + resource.Resource, + drawable.Drawable, + drawable.Window, + drawable.Pixmap, + fontable.Fontable, + fontable.Font, + fontable.GC, + colormap.Colormap, + cursor.Cursor, + ] + _resource_baseclasses = { 'resource': resource.Resource, 'drawable': drawable.Drawable, @@ -67,11 +89,13 @@ class _BaseDisplay(protocol_display.Display): # dealing with some ICCCM properties not defined in Xlib.Xatom def __init__(self, *args, **keys): + # type: (str | None) -> None self.resource_classes = _resource_baseclasses.copy() protocol_display.Display.__init__(self, *args, **keys) self._atom_cache = {} def get_atom(self, atomname, only_if_exists=False): + # type: (str, bool) -> int if atomname in self._atom_cache: return self._atom_cache[atomname] @@ -86,22 +110,23 @@ def get_atom(self, atomname, only_if_exists=False): class Display(object): def __init__(self, display = None): + # type: (str | None) -> None self.display = _BaseDisplay(display) # Create the keymap cache - self._keymap_codes = [()] * 256 - self._keymap_syms = {} + self._keymap_codes = [()] * 256 # type: list[tuple[int, ...]] + self._keymap_syms = {} # type: dict[int, list[tuple[int, ...]]] self._update_keymap(self.display.info.min_keycode, (self.display.info.max_keycode - self.display.info.min_keycode + 1)) # Translations for keysyms to strings. - self.keysym_translations = {} + self.keysym_translations = {} # type: dict[int, str] # Find all supported extensions - self.extensions = [] - self.class_extension_dicts = {} - self.display_extension_methods = {} + self.extensions = [] # type: list[str] + self.class_extension_dicts = {} # type: dict[str, dict[str, types.FunctionType]] + self.display_extension_methods = {} # type: dict[str, Callable[..., Any]] # a dict that maps the event name to the code # or, when it's an event with a subcode, to a tuple of (event,subcode) @@ -161,6 +186,7 @@ def close(self): self.display.close() def set_error_handler(self, handler): + # type: (_ErrorHandler[object] | None) -> None """Set the default error handler which will be called for all unhandled errors. handler should take two arguments as a normal request error handler, but the second argument (the request) will @@ -192,11 +218,54 @@ def pending_events(self): return self.display.pending_events() def has_extension(self, extension): + # type: (str) -> bool """Check if both the server and the client library support the X extension named extension.""" return extension in self.extensions + if TYPE_CHECKING: + @overload + def create_resource_object(self, type, id): + # type: (Literal['resource'], int) -> resource.Resource + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['drawable'], int) -> resource.Drawable + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['window'], int) -> resource.Window + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['pixmap'], int) -> resource.Pixmap + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['fontable'], int) -> resource.Fontable + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['font'], int) -> resource.Font + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['gc'], int) -> resource.GC + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['colormap'], int) -> resource.Colormap + pass + @overload + def create_resource_object(self, type, id): + # type: (Literal['cursor'], int) -> resource.Cursor + pass + @overload + def create_resource_object(self, type, id): + # type: (str, int) -> resource.Resource + pass def create_resource_object(self, type, id): + # type: (str, int) -> resource.Resource """Create a resource object of type for the integer id. type should be one of the following strings: @@ -220,6 +289,7 @@ class directly, since any X extensions dynamically added by the # We need this to handle display extension methods def __getattr__(self, attr): + # type: (str) -> types.MethodType try: function = self.display_extension_methods[attr] return types.MethodType(function, self) @@ -231,6 +301,7 @@ def __getattr__(self, attr): ### def screen(self, sno = None): + # type: (int | None) -> rq.Struct if sno is None: return self.display.info.roots[self.display.default_screen] else: @@ -250,6 +321,7 @@ def get_default_screen(self): ### def extension_add_method(self, object, name, function): + # type: (str, str, Callable[..., Any]) -> None """extension_add_method(object, name, function) Add an X extension module method. OBJECT is the type of @@ -292,6 +364,7 @@ def extension_add_method(self, object, name, function): self.class_extension_dicts[class_name] = { name: method } def extension_add_event(self, code, evt, name = None): + # type: (int, type, str | None) -> None """extension_add_event(code, evt, [name]) Add an extension event. CODE is the numeric code, and EVT is @@ -315,6 +388,7 @@ def extension_add_event(self, code, evt, name = None): setattr(self.extension_event, name, code) def extension_add_subevent(self, code, subcode, evt, name = None): + # type: (int, int | None, type[rq.Event], str | None) -> None """extension_add_subevent(code, evt, [name]) Add an extension subevent. CODE is the numeric code, subcode @@ -341,6 +415,7 @@ def extension_add_subevent(self, code, subcode, evt, name = None): setattr(self.extension_event, name, (code,subcode)) def extension_add_error(self, code, err): + # type: (int, type[error.XError]) -> None """extension_add_error(code, err) Add an extension error. CODE is the numeric code, and ERR is @@ -364,6 +439,7 @@ def extension_add_error(self, code, err): # index is the keysyms index in the map for that keycode. def keycode_to_keysym(self, keycode, index): + # type: (int, int) -> int """Convert a keycode to a keysym, looking in entry index. Normally index 0 is unshifted, 1 is shifted, 2 is alt grid, and 3 is shift+alt grid. If that key entry is not bound, X.NoSymbol is @@ -374,6 +450,7 @@ def keycode_to_keysym(self, keycode, index): return X.NoSymbol def keysym_to_keycode(self, keysym): + # type: (int) -> int """Look up the primary keycode that is bound to keysym. If several keycodes are found, the one with the lowest index and lowest code is returned. If keysym is not bound to any key, 0 is @@ -384,6 +461,7 @@ def keysym_to_keycode(self, keysym): return 0 def keysym_to_keycodes(self, keysym): + # type: (int) -> map[tuple[int, int]] | list[tuple[int, int]] """Look up all the keycodes that is bound to keysym. A list of tuples (keycode, index) is returned, sorted primarily on the lowest index and secondarily on the lowest keycode.""" @@ -394,6 +472,7 @@ def keysym_to_keycodes(self, keysym): return [] def refresh_keyboard_mapping(self, evt): + # type: (rq.Event) -> None """This method should be called once when a MappingNotify event is received, to update the keymap cache. evt should be the event object.""" @@ -446,6 +525,7 @@ def _update_keymap(self, first_keycode, count): ### def lookup_string(self, keysym): + # type: (int) -> str | None """Return a string corresponding to KEYSYM, or None if no reasonable translation is found. """ @@ -457,6 +537,7 @@ def lookup_string(self, keysym): return Xlib.XK.keysym_to_string(keysym) def rebind_string(self, keysym, newstring): + # type: (int, str | None) -> None """Change the translation of KEYSYM to NEWSTRING. If NEWSTRING is None, remove old translation if any. """ @@ -474,6 +555,7 @@ def rebind_string(self, keysym, newstring): ### def intern_atom(self, name, only_if_exists = False): + # type: (str, bool) -> int """Intern the string name, returning its atom number. If only_if_exists is true and the atom does not already exist, it will not be created and X.NONE is returned.""" @@ -483,11 +565,13 @@ def intern_atom(self, name, only_if_exists = False): return r.atom def get_atom(self, atom, only_if_exists = False): + # type: (str, bool) -> int """Alias for intern_atom, using internal cache""" return self.display.get_atom(atom, only_if_exists) def get_atom_name(self, atom): + # type: (int) -> str """Look up the name of atom, returning it as a string. Will raise BadAtom if atom does not exist.""" r = request.GetAtomName(display = self.display, @@ -495,6 +579,7 @@ def get_atom_name(self, atom): return r.name def get_selection_owner(self, selection): + # type: (int) -> int """Return the window that owns selection (an atom), or X.NONE if there is no owner for the selection. Can raise BadAtom.""" r = request.GetSelectionOwner(display = self.display, @@ -503,6 +588,7 @@ def get_selection_owner(self, selection): def send_event(self, destination, event, event_mask = 0, propagate = False, onerror = None): + # type: (int, rq.Event, int, bool, _ErrorHandler[object] | None) -> None """Send a synthetic event to the window destination which can be a window object, or X.PointerWindow or X.InputFocus. event is the event object to send, instantiated from one of the classes in @@ -517,6 +603,7 @@ def send_event(self, destination, event, event_mask = 0, propagate = False, event = event) def ungrab_pointer(self, time, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """Release a grabbed pointer and any queued events. See XUngrabPointer(3X11).""" request.UngrabPointer(display = self.display, @@ -524,6 +611,7 @@ def ungrab_pointer(self, time, onerror = None): time = time) def change_active_pointer_grab(self, event_mask, cursor, time, onerror = None): + # type: (int, cursor.Cursor, int, _ErrorHandler[object] | None) -> None """Change the dynamic parameters of a pointer grab. See XChangeActivePointerGrab(3X11).""" request.ChangeActivePointerGrab(display = self.display, @@ -533,6 +621,7 @@ def change_active_pointer_grab(self, event_mask, cursor, time, onerror = None): event_mask = event_mask) def ungrab_keyboard(self, time, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """Ungrab a grabbed keyboard and any queued events. See XUngrabKeyboard(3X11).""" request.UngrabKeyboard(display = self.display, @@ -540,6 +629,7 @@ def ungrab_keyboard(self, time, onerror = None): time = time) def allow_events(self, mode, time, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None """Release some queued events. mode should be one of X.AsyncPointer, X.SyncPointer, X.AsyncKeyboard, X.SyncKeyboard, X.ReplayPointer, X.ReplayKeyboard, X.AsyncBoth, or X.SyncBoth. @@ -550,6 +640,7 @@ def allow_events(self, mode, time, onerror = None): time = time) def grab_server(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None """Disable processing of requests on all other client connections until the server is ungrabbed. Server grabbing should be avoided as much as possible.""" @@ -557,12 +648,14 @@ def grab_server(self, onerror = None): onerror = onerror) def ungrab_server(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None """Release the server if it was previously grabbed by this client.""" request.UngrabServer(display = self.display, onerror = onerror) def warp_pointer(self, x, y, src_window = X.NONE, src_x = 0, src_y = 0, src_width = 0, src_height = 0, onerror = None): + # type: (int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None """Move the pointer relative its current position by the offsets (x, y). However, if src_window is a window the pointer is only moved if the specified rectangle in src_window contains it. If @@ -582,6 +675,7 @@ def warp_pointer(self, x, y, src_window = X.NONE, src_x = 0, src_y = 0, dst_y = y) def set_input_focus(self, focus, revert_to, time, onerror = None): + # type: (int, int, int, _ErrorHandler[object] | None) -> None """Set input focus to focus, which should be a window, X.PointerRoot or X.NONE. revert_to specifies where the focus reverts to if the focused window becomes not visible, and should @@ -596,6 +690,7 @@ def set_input_focus(self, focus, revert_to, time, onerror = None): time = time) def get_input_focus(self): + # type: () -> request.GetInputFocus """Return an object with the following attributes: focus @@ -607,6 +702,7 @@ def get_input_focus(self): return request.GetInputFocus(display = self.display) def query_keymap(self): + # type: () -> bytes # TODO: Validate if this is correct """Return a bit vector for the logical state of the keyboard, where each bit set to 1 indicates that the corresponding key is currently pressed down. The vector is represented as a list of 32 @@ -616,6 +712,7 @@ def query_keymap(self): return r.map def open_font(self, name): + # type: (str) -> _ResourceBaseClass | None """Open the font identifed by the pattern name and return its font object. If name does not match any font, None is returned.""" fid = self.display.allocate_resource_id() @@ -635,6 +732,7 @@ def open_font(self, name): return cls(self.display, fid, owner = 1) def list_fonts(self, pattern, max_names): + # type: (Pattern[str] | str, int) -> list[str] """Return a list of font names matching pattern. No more than max_names will be returned.""" r = request.ListFonts(display = self.display, @@ -643,6 +741,7 @@ def list_fonts(self, pattern, max_names): return r.fonts def list_fonts_with_info(self, pattern, max_names): + # type: (Pattern[str] | str, int) -> request.ListFontsWithInfo """Return a list of fonts matching pattern. No more than max_names will be returned. Each list item represents one font and has the following properties: @@ -676,6 +775,7 @@ def list_fonts_with_info(self, pattern, max_names): pattern = pattern) def set_font_path(self, path, onerror = None): + # type: (Sequence[str], _ErrorHandler[object] | None) -> None """Set the font path to path, which should be a list of strings. If path is empty, the default font path of the server will be restored.""" @@ -684,11 +784,13 @@ def set_font_path(self, path, onerror = None): path = path) def get_font_path(self): + # type: () -> list[str] """Return the current font path as a list of strings.""" r = request.GetFontPath(display = self.display) return r.paths def query_extension(self, name): + # type: (str) -> request.QueryExtension | None """Ask the server if it supports the extension name. If it is supported an object with the following attributes is returned: @@ -708,11 +810,13 @@ def query_extension(self, name): return None def list_extensions(self): + # type: () -> list[str] """Return a list of all the extensions provided by the server.""" r = request.ListExtensions(display = self.display) return r.names def change_keyboard_mapping(self, first_keycode, keysyms, onerror = None): + # type: (int, Sequence[Sequence[int]], _ErrorHandler[object] | None) -> None """Modify the keyboard mapping, starting with first_keycode. keysyms is a list of tuples of keysyms. keysyms[n][i] will be assigned to keycode first_keycode+n at index i.""" @@ -722,6 +826,7 @@ def change_keyboard_mapping(self, first_keycode, keysyms, onerror = None): keysyms = keysyms) def get_keyboard_mapping(self, first_keycode, count): + # type: (int, int) -> list[tuple[int, ...]] """Return the current keyboard mapping as a list of tuples, starting at first_keycount and no more than count.""" r = request.GetKeyboardMapping(display = self.display, @@ -730,6 +835,7 @@ def get_keyboard_mapping(self, first_keycode, count): return r.keysyms def change_keyboard_control(self, onerror = None, **keys): + # type: (_ErrorHandler[object] | None, object) -> None """Change the parameters provided as keyword arguments: key_click_percent @@ -786,6 +892,7 @@ def get_keyboard_control(self): return request.GetKeyboardControl(display = self.display) def bell(self, percent = 0, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """Ring the bell at the volume percent which is relative the base volume. See XBell(3X11).""" request.Bell(display = self.display, @@ -793,6 +900,7 @@ def bell(self, percent = 0, onerror = None): percent = percent) def change_pointer_control(self, accel = None, threshold = None, onerror = None): + # type: (tuple[int, int] | None, int | None, _ErrorHandler[object] | None) -> None """To change the pointer acceleration, set accel to a tuple (num, denum). The pointer will then move num/denum times the normal speed if it moves beyond the threshold number of pixels at once. @@ -833,6 +941,7 @@ def get_pointer_control(self): return request.GetPointerControl(display = self.display) def set_screen_saver(self, timeout, interval, prefer_blank, allow_exposures, onerror = None): + # type: (int, int, int, int, _ErrorHandler[object] | None) -> None """See XSetScreenSaver(3X11).""" request.SetScreenSaver(display = self.display, onerror = onerror, @@ -848,6 +957,7 @@ def get_screen_saver(self): return request.GetScreenSaver(display = self.display) def change_hosts(self, mode, host_family, host, onerror = None): + # type: (int, int, Sequence[int] | Sequence[bytes], _ErrorHandler[object] | None) -> None # TODO: validate the list of bytes """mode is either X.HostInsert or X.HostDelete. host_family is one of X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, X.FamilyServerInterpreted or X.FamilyInternetV6. @@ -877,6 +987,7 @@ def list_hosts(self): return request.ListHosts(display = self.display) def set_access_control(self, mode, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """Enable use of access control lists at connection setup if mode is X.EnableAccess, disable if it is X.DisableAccess.""" request.SetAccessControl(display = self.display, @@ -884,6 +995,7 @@ def set_access_control(self, mode, onerror = None): mode = mode) def set_close_down_mode(self, mode, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """Control what will happen with the client's resources at connection close. The default is X.DestroyAll, the other values are X.RetainPermanent and X.RetainTemporary.""" @@ -892,6 +1004,7 @@ def set_close_down_mode(self, mode, onerror = None): mode = mode) def force_screen_saver(self, mode, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None """If mode is X.ScreenSaverActive the screen saver is activated. If it is X.ScreenSaverReset, the screen saver is deactivated as if device input had been received.""" @@ -900,6 +1013,7 @@ def force_screen_saver(self, mode, onerror = None): mode = mode) def set_pointer_mapping(self, map): + # type: (Sequence[int]) -> int """Set the mapping of the pointer buttons. map is a list of logical button numbers. map must be of the same length as the list returned by Display.get_pointer_mapping(). @@ -918,12 +1032,14 @@ def set_pointer_mapping(self, map): return r.status def get_pointer_mapping(self): + # type: () -> list[int] """Return a list of the pointer button mappings. Entry N in the list sets the logical button number for the physical button N+1.""" r = request.GetPointerMapping(display = self.display) return r.map def set_modifier_mapping(self, keycodes): + # type: (Sequence[Sequence[int]] | tuple[Sequence[int], Sequence[int], Sequence[int], Sequence[int], Sequence[int], Sequence[int], Sequence[int], Sequence[int]]) -> int """Set the keycodes for the eight modifiers X.Shift, X.Lock, X.Control, X.Mod1, X.Mod2, X.Mod3, X.Mod4 and X.Mod5. keycodes should be a eight-element list where each entry is a list of the @@ -939,6 +1055,7 @@ def set_modifier_mapping(self, keycodes): return r.status def get_modifier_mapping(self): + # type: () -> list[list[int]] """Return a list of eight lists, one for each modifier. The list can be indexed using X.ShiftMapIndex, X.Mod1MapIndex, and so on. The sublists list the keycodes bound to that modifier.""" @@ -946,6 +1063,7 @@ def get_modifier_mapping(self): return r.keycodes def no_operation(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None """Do nothing but send a request to the server.""" request.NoOperation(display = self.display, onerror = onerror) diff --git a/Xlib/error.py b/Xlib/error.py index 3b6e13f..d34d0d9 100644 --- a/Xlib/error.py +++ b/Xlib/error.py @@ -25,9 +25,20 @@ # Xlib.protocol modules from .protocol import rq +try: + from typing import TYPE_CHECKING, Any, Union +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from mmap import mmap + from array import array + from typing_extensions import Literal + from Xlib.protocol import display + _SliceableBuffer = Union[bytes, bytearray, memoryview, array[Any], mmap] class DisplayError(Exception): def __init__(self, display): + # type: (object) -> None self.display = display def __str__(self): @@ -39,6 +50,7 @@ def __str__(self): class DisplayConnectionError(DisplayError): def __init__(self, display, msg): + # type: (object, object) -> None self.display = display self.msg = msg @@ -47,6 +59,7 @@ def __str__(self): class ConnectionClosedError(Exception): def __init__(self, whom): + # type: (object) -> None self.whom = whom def __str__(self): @@ -70,6 +83,7 @@ class XError(rq.GetAttrData, Exception): ) def __init__(self, display, data): + # type: (display.Display, _SliceableBuffer) -> None self._data, _ = self._fields.parse_binary(data, display, rawdict = True) def __str__(self): @@ -131,11 +145,13 @@ class BadImplementation(XError): pass class CatchError(object): def __init__(self, *errors): + # type: (type[XError]) -> None self.error_types = errors - self.error = None - self.request = None + self.error = None # type: XError | None + self.request = None # type: rq.Request | None def __call__(self, error, request): + # type: (XError, rq.Request | None) -> Literal[0, 1] if self.error_types: for etype in self.error_types: if isinstance(error, etype): diff --git a/Xlib/ext/composite.py b/Xlib/ext/composite.py index 5909b31..93d7b68 100644 --- a/Xlib/ext/composite.py +++ b/Xlib/ext/composite.py @@ -36,6 +36,19 @@ from Xlib.protocol import rq from Xlib.xobject import drawable +try: + from typing import TYPE_CHECKING, TypeVar, Optional, Union +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from Xlib.error import XError + from Xlib.display import Display + from Xlib.xobject import resource + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[rq.Request]], _T] + _Update = Callable[[Union[rq.DictWrapper, dict[str, object]]], object] + extname = 'Composite' RedirectAutomatic = 0 @@ -61,6 +74,7 @@ class QueryVersion(rq.ReplyRequest): ) def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion( display = self.display, opcode = self.display.get_extension_major(extname), @@ -80,6 +94,7 @@ class RedirectWindow(rq.Request): ) def redirect_window(self, update, onerror = None): + # type: (drawable.Window, _Update, _ErrorHandler[object] | None) -> None """Redirect the hierarchy starting at this window to off-screen storage. """ @@ -102,6 +117,7 @@ class RedirectSubwindows(rq.Request): ) def redirect_subwindows(self, update, onerror = None): + # type: (drawable.Window, _Update, _ErrorHandler[object] | None) -> None """Redirect the hierarchies starting at all current and future children to this window to off-screen storage. """ @@ -124,6 +140,7 @@ class UnredirectWindow(rq.Request): ) def unredirect_window(self, update, onerror = None): + # type: (drawable.Window, _Update, _ErrorHandler[object] | None) -> None """Stop redirecting this window hierarchy. """ UnredirectWindow(display = self.display, @@ -145,6 +162,7 @@ class UnredirectSubindows(rq.Request): ) def unredirect_subwindows(self, update, onerror = None): + # type: (drawable.Window, _Update, _ErrorHandler[object] | None) -> None """Stop redirecting the hierarchies of children to this window. """ RedirectWindow(display = self.display, @@ -165,6 +183,7 @@ class CreateRegionFromBorderClip(rq.Request): ) def create_region_from_border_clip(self, onerror = None): + # type: (drawable.Window, _ErrorHandler[object] | None) -> int """Create a region of the border clip of the window, i.e. the area that is not clipped by the parent and any sibling windows. """ @@ -192,6 +211,7 @@ class NameWindowPixmap(rq.Request): ) def name_window_pixmap(self, onerror = None): + # type: (drawable.Window, _ErrorHandler[object] | None) -> drawable.Pixmap """Create a new pixmap that refers to the off-screen storage of the window, including its border. @@ -230,6 +250,7 @@ class GetOverlayWindow(rq.ReplyRequest): ) def get_overlay_window(self): + # type: (Display | resource.Resource) -> GetOverlayWindow """Return the overlay window of the root window. """ @@ -238,6 +259,7 @@ def get_overlay_window(self): window = self) def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'composite_query_version', query_version) diff --git a/Xlib/ext/damage.py b/Xlib/ext/damage.py index 126d850..c4bc902 100644 --- a/Xlib/ext/damage.py +++ b/Xlib/ext/damage.py @@ -21,8 +21,10 @@ from Xlib import X -from Xlib.protocol import rq, structs +from Xlib.protocol import rq, structs, request +from Xlib.xobject import resource from Xlib.error import XError +from Xlib.display import Display extname = 'DAMAGE' @@ -70,6 +72,7 @@ class QueryVersion(rq.ReplyRequest): ) def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -86,6 +89,7 @@ class DamageCreate(rq.Request): ) def damage_create(self, level): + # type: (Display | resource.Resource, int) -> int did = self.display.allocate_resource_id() DamageCreate(display=self.display, opcode=self.display.get_extension_major(extname), @@ -103,6 +107,7 @@ class DamageDestroy(rq.Request): ) def damage_destroy(self, damage): + # type: (Display | resource.Resource, int) -> None DamageDestroy(display=self.display, opcode=self.display.get_extension_major(extname), damage=damage, @@ -120,6 +125,7 @@ class DamageSubtract(rq.Request): ) def damage_subtract(self, damage, repair=X.NONE, parts=X.NONE): + # type: (Display | resource.Resource, int, int, int) -> None DamageSubtract(display=self.display, opcode=self.display.get_extension_major(extname), damage=damage, @@ -135,6 +141,7 @@ class DamageAdd(rq.Request): ) def damage_add(self, repair, parts): + # type: (Display | resource.Resource, int, int) -> None DamageAdd(display=self.display, opcode=self.display.get_extension_major(extname), repair=repair, @@ -156,6 +163,7 @@ class DamageNotify(rq.Event): ) def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('display', 'damage_query_version', query_version) diff --git a/Xlib/ext/dpms.py b/Xlib/ext/dpms.py index 20b570b..74ed9ca 100644 --- a/Xlib/ext/dpms.py +++ b/Xlib/ext/dpms.py @@ -28,6 +28,8 @@ ''' from Xlib.protocol import rq +from Xlib.display import Display +from Xlib.xobject import resource extname = 'DPMS' @@ -71,6 +73,7 @@ class DPMSGetVersion(rq.ReplyRequest): def get_version(self): + # type: (Display | resource.Resource) -> DPMSGetVersion return DPMSGetVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -95,6 +98,7 @@ class DPMSCapable(rq.ReplyRequest): def capable(self): + # type: (Display | resource.Resource) -> DPMSCapable return DPMSCapable(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -121,6 +125,7 @@ class DPMSGetTimeouts(rq.ReplyRequest): def get_timeouts(self): + # type: (Display | resource.Resource) -> DPMSGetTimeouts return DPMSGetTimeouts(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -140,6 +145,7 @@ class DPMSSetTimeouts(rq.Request): def set_timeouts(self, standby_timeout, suspend_timeout, off_timeout): + # type: (Display | resource.Resource, int, int, int) -> DPMSSetTimeouts return DPMSSetTimeouts(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -158,6 +164,7 @@ class DPMSEnable(rq.Request): def enable(self): + # type: (Display | resource.Resource) -> DPMSEnable return DPMSEnable(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -173,6 +180,7 @@ class DPMSDisable(rq.Request): def disable(self): + # type: (Display | resource.Resource) -> DPMSDisable return DPMSDisable(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -189,6 +197,7 @@ class DPMSForceLevel(rq.Request): def force_level(self, power_level): + # type: (Display | resource.Resource, int) -> DPMSForceLevel return DPMSForceLevel(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -215,6 +224,7 @@ class DPMSInfo(rq.ReplyRequest): def info(self): + # type: (Display | resource.Resource) -> DPMSInfo return DPMSInfo(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -222,6 +232,7 @@ def info(self): def init(disp, _info): + # type: (Display, object) -> None disp.extension_add_method('display', 'dpms_get_version', get_version) disp.extension_add_method('display', 'dpms_capable', capable) disp.extension_add_method('display', 'dpms_get_timeouts', get_timeouts) diff --git a/Xlib/ext/ge.py b/Xlib/ext/ge.py index 291ffa9..62355a5 100644 --- a/Xlib/ext/ge.py +++ b/Xlib/ext/ge.py @@ -26,6 +26,18 @@ from Xlib.protocol import rq +try: + from typing import TYPE_CHECKING, Union, Any +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.protocol import display + from Xlib.xobject import resource + from mmap import mmap + from array import array + _SliceableBuffer = Union[bytes, bytearray, memoryview, array[Any], mmap] + extname = 'Generic Event Extension' @@ -52,6 +64,7 @@ class GEQueryVersion(rq.ReplyRequest): def query_version(self): + # type: (Display | resource.Resource) -> GEQueryVersion return GEQueryVersion( display=self.display, opcode=self.display.get_extension_major(extname), @@ -77,6 +90,7 @@ class GenericEvent(rq.Event): ) def __init__(self, binarydata = None, display = None, **keys): + # type: (_SliceableBuffer | None, display.Display | None, object) -> None if binarydata: data = binarydata[10:] binarydata = binarydata[:10] @@ -101,12 +115,14 @@ def __init__(self, binarydata = None, display = None, **keys): def add_event_data(self, extension, evtype, estruct): + # type: (Display | resource.Resource, int, int, int) -> None if not hasattr(self.display, 'ge_event_data'): self.display.ge_event_data = {} self.display.ge_event_data[(extension, evtype)] = estruct def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'ge_query_version', query_version) disp.extension_add_method('display', 'ge_add_event_data', add_event_data) disp.extension_add_event(GenericEventCode, GenericEvent) diff --git a/Xlib/ext/nvcontrol.py b/Xlib/ext/nvcontrol.py index 7a21826..6d09821 100644 --- a/Xlib/ext/nvcontrol.py +++ b/Xlib/ext/nvcontrol.py @@ -23,11 +23,14 @@ """NV-CONTROL - provide access to the NV-CONTROL extension information.""" from Xlib.protocol import rq +from Xlib.display import Display +from Xlib.xobject import resource extname = 'NV-CONTROL' def query_target_count(self, target): + # type: (Display | resource.Resource, Target) -> int """Return the target count""" reply = NVCtrlQueryTargetCountReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -36,6 +39,7 @@ def query_target_count(self, target): def query_int_attribute(self, target, display_mask, attr): + # type: (Display | resource.Resource, Target, int, int) -> int | None """Return the value of an integer attribute""" reply = NVCtrlQueryAttributeReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -49,6 +53,7 @@ def query_int_attribute(self, target, display_mask, attr): def set_int_attribute(self, target, display_mask, attr, value): + # type: (Display | resource.Resource, Target, int, int, int) -> bool """Set the value of an integer attribute""" reply = NVCtrlSetAttributeAndGetStatusReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -61,6 +66,7 @@ def set_int_attribute(self, target, display_mask, attr, value): def query_string_attribute(self, target, display_mask, attr): + # type: (Display | resource.Resource, Target, int, int) -> str | None """Return the value of a string attribute""" reply = NVCtrlQueryStringAttributeReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -74,6 +80,7 @@ def query_string_attribute(self, target, display_mask, attr): def query_valid_attr_values(self, target, display_mask, attr): + # type: (Display | resource.Resource, Target, int, int) -> tuple[int, int] | None """Return the value of an integer attribute""" reply = NVCtrlQueryValidAttributeValuesReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -87,6 +94,7 @@ def query_valid_attr_values(self, target, display_mask, attr): def query_binary_data(self, target, display_mask, attr): + # type: (Display | resource.Resource, Target, int, int) -> bytes | None """Return binary data""" reply = NVCtrlQueryBinaryDataReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), @@ -100,6 +108,7 @@ def query_binary_data(self, target, display_mask, attr): def get_coolers_used_by_gpu(self, target): + # type: (Display | resource.Resource, Target) -> bytes | None reply = NVCtrlQueryListCard32ReplyRequest(display=self.display, opcode=self.display.get_extension_major(extname), target_id=target.id(), @@ -116,30 +125,36 @@ def get_coolers_used_by_gpu(self, target): def get_gpu_count(self): + # type: (Display | resource.Resource) -> int """Return the number of GPU's present in the system.""" return int(query_target_count(self, Gpu())) def get_name(self, target): + # type: (Display | resource.Resource, Target) -> str | None """Return the GPU product name on which the specified X screen is running""" return query_string_attribute(self, target, 0, NV_CTRL_STRING_PRODUCT_NAME) def get_driver_version(self, target): + # type: (Display | resource.Resource, Target) -> str | None """Return the NVIDIA (kernel level) driver version for the specified screen or GPU""" return query_string_attribute(self, target, 0, NV_CTRL_STRING_NVIDIA_DRIVER_VERSION) def get_vbios_version(self, target): + # type: (Display | resource.Resource, Target) -> str | None """Return the version of the VBIOS for the specified screen or GPU""" return query_string_attribute(self, target, 0, NV_CTRL_STRING_VBIOS_VERSION) def get_gpu_uuid(self, target): + # type: (Display | resource.Resource, Target) -> str | None return query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UUID) def get_utilization_rates(self, target): + # type: (Display | resource.Resource, Target) -> dict[str, str | int] string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UTILIZATION) result = {} if string is not None and string != '': @@ -150,6 +165,7 @@ def get_utilization_rates(self, target): def get_performance_modes(self, target): + # type: (Display | resource.Resource, Target) -> list[dict[str, str | int]] string = query_string_attribute(self, target, 0, NV_CTRL_STRING_PERFORMANCE_MODES) result = [] if string is not None and string != '': @@ -163,6 +179,7 @@ def get_performance_modes(self, target): def get_clock_info(self, target): + # type: (Display | resource.Resource, Target) -> dict[str, str | int] string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS) result = {} if string is not None and string != '': @@ -173,15 +190,18 @@ def get_clock_info(self, target): def get_vram(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_RAM) def get_irq(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the interrupt request line used by the GPU driving the screen""" return query_int_attribute(self, target, 0, NV_CTRL_IRQ) def supports_framelock(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return whether the underlying GPU supports Frame Lock. All of the other frame lock attributes are only applicable if this returns True. @@ -190,6 +210,7 @@ def supports_framelock(self, target): def gvo_supported(self, screen): + # type: (Display | resource.Resource, Target) -> int | None """Return whether this X screen supports GVO If this screen does not support GVO output, then all other GVO attributes are unavailable. @@ -198,11 +219,13 @@ def gvo_supported(self, screen): def get_core_temp(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the current core temperature of the GPU driving the X screen.""" return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_TEMPERATURE) def get_core_threshold(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the current GPU core slowdown threshold temperature. It reflects the temperature at which the GPU is throttled to prevent overheating. @@ -211,113 +234,140 @@ def get_core_threshold(self, target): def get_default_core_threshold(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the default core threshold temperature.""" return query_int_attribute(self, target, 0, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD) def get_max_core_threshold(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the maximum core threshold temperature.""" return query_int_attribute(self, target, 0, NV_CTRL_GPU_MAX_CORE_THRESHOLD) def get_ambient_temp(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the current temperature in the immediate neighbourhood of the GPU driving the X screen.""" return query_int_attribute(self, target, 0, NV_CTRL_AMBIENT_TEMPERATURE) def get_cuda_cores(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORES) def get_memory_bus_width(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_MEMORY_BUS_WIDTH) def get_total_dedicated_gpu_memory(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY) def get_used_dedicated_gpu_memory(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_USED_DEDICATED_GPU_MEMORY) def get_curr_pcie_link_width(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH) def get_max_pcie_link_width(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH) def get_curr_pcie_link_generation(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_GENERATION) def get_encoder_utilization(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_ENCODER_UTILIZATION) def get_decoder_utilization(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_DECODER_UTILIZATION) def get_current_performance_level(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL) def get_gpu_nvclock_offset(self, target, perf_level): + # type: (Display | resource.Resource, Target, int) -> int | None return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) def set_gpu_nvclock_offset(self, target, perf_level, offset): + # type: (Display | resource.Resource, Target, int, int) -> bool return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET, offset) def set_gpu_nvclock_offset_all_levels(self, target, offset): + # type: (Display | resource.Resource, Target, int) -> bool return set_int_attribute(self, target, 0, NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS, offset) def get_gpu_nvclock_offset_range(self, target, perf_level): + # type: (Display | resource.Resource, Target, int) -> tuple[int, int] | None return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) def get_mem_transfer_rate_offset(self, target, perf_level): + # type: (Display | resource.Resource, Target, int) -> int | None return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) def set_mem_transfer_rate_offset(self, target, perf_level, offset): + # type: (Display | resource.Resource, Target, int, int) -> bool return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET, offset) def set_mem_transfer_rate_offset_all_levels(self, target, offset): + # type: (Display | resource.Resource, Target, int) -> bool return set_int_attribute(self, target, 0, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS, offset) def get_mem_transfer_rate_offset_range(self, target, perf_level): + # type: (Display | resource.Resource, Target, int) -> tuple[int, int] | None return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) def get_cooler_manual_control_enabled(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL) def set_cooler_manual_control_enabled(self, target, enabled): + # type: (Display | resource.Resource, Target, bool) -> bool return set_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, 1 if enabled else 0) == 1 def get_fan_duty(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL) def set_fan_duty(self, cooler, speed): + # type: (Display | resource.Resource, Target, int) -> bool return set_int_attribute(self, cooler, 0, NV_CTRL_THERMAL_COOLER_LEVEL, speed) def get_fan_rpm(self, target): + # type: (Display | resource.Resource, Target) -> int | None return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_SPEED) def get_max_displays(self, target): + # type: (Display | resource.Resource, Target) -> int | None """Return the maximum number of display devices that can be driven simultaneously on a GPU. Note that this does not indicate the maximum number of bits that can be set in @@ -354,6 +404,7 @@ def _displays2mask(displays): def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'nvcontrol_query_target_count', query_target_count) disp.extension_add_method('display', 'nvcontrol_query_int_attribute', query_int_attribute) disp.extension_add_method('display', 'nvcontrol_query_string_attribute', query_string_attribute) @@ -5199,6 +5250,7 @@ def __str__(self): class Gpu(Target): def __init__(self, ngpu=0): + # type: (int) -> None """Target a GPU""" super(self.__class__, self).__init__() self._id = ngpu @@ -5208,6 +5260,7 @@ def __init__(self, ngpu=0): class Screen(Target): def __init__(self, nscr=0): + # type: (int) -> None """Target an X screen""" super(self.__class__, self).__init__() self._id = nscr @@ -5217,6 +5270,7 @@ def __init__(self, nscr=0): class Cooler(Target): def __init__(self, nfan=0): + # type: (int) -> None """Target a fann""" super(self.__class__, self).__init__() self._id = nfan diff --git a/Xlib/ext/randr.py b/Xlib/ext/randr.py index 9cbfe2d..b866639 100644 --- a/Xlib/ext/randr.py +++ b/Xlib/ext/randr.py @@ -36,6 +36,15 @@ from Xlib import X from Xlib.protocol import rq +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.protocol import request + from Xlib.xobject import resource, drawable + from collections.abc import Sequence extname = 'RANDR' @@ -209,6 +218,7 @@ class QueryVersion(rq.ReplyRequest): ) def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion """Get the current version of the RandR extension. """ @@ -284,6 +294,7 @@ class SetScreenConfig(rq.ReplyRequest): ) def set_screen_config(self, size_id, rotation, config_timestamp, rate=0, timestamp=X.CurrentTime): + # type: (drawable.Drawable, int, int, int, int, int) -> SetScreenConfig """Sets the screen to the specified size, rate, rotation and reflection. rate can be 0 to have the server select an appropriate rate. @@ -312,6 +323,7 @@ class SelectInput(rq.Request): ) def select_input(self, mask): + # type: (drawable.Window, int) -> SelectInput return SelectInput( display=self.display, opcode=self.display.get_extension_major(extname), @@ -346,6 +358,7 @@ class GetScreenInfo(rq.ReplyRequest): ) def get_screen_info(self): + # type: (drawable.Window) -> GetScreenInfo """Retrieve information about the current and available configurations for the screen associated with this window. @@ -379,6 +392,7 @@ class GetScreenSizeRange(rq.ReplyRequest): ) def get_screen_size_range(self): + # type: (drawable.Window) -> GetScreenSizeRange """Retrieve the range of possible screen sizes. The screen may be set to any size within this range. @@ -403,6 +417,7 @@ class SetScreenSize(rq.Request): ) def set_screen_size(self, width, height, width_in_millimeters=None, height_in_millimeters=None): + # type: (drawable.Window, int, int, int | None, int | None) -> SetScreenSize return SetScreenSize( display=self.display, opcode=self.display.get_extension_major(extname), @@ -440,6 +455,7 @@ class GetScreenResources(rq.ReplyRequest): ) def get_screen_resources(self): + # type: (drawable.Window) -> GetScreenResources return GetScreenResources( display=self.display, opcode=self.display.get_extension_major(extname), @@ -478,6 +494,7 @@ class GetOutputInfo(rq.ReplyRequest): ) def get_output_info(self, output, config_timestamp): + # type: (Display | resource.Resource, int, int) -> GetOutputInfo return GetOutputInfo( display=self.display, opcode=self.display.get_extension_major(extname), @@ -504,6 +521,7 @@ class ListOutputProperties(rq.ReplyRequest): ) def list_output_properties(self, output): + # type: (Display | resource.Resource, int) -> ListOutputProperties return ListOutputProperties ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -532,6 +550,7 @@ class QueryOutputProperty(rq.ReplyRequest): ) def query_output_property(self, output, property): + # type: (Display | resource.Resource, int, int) -> QueryOutputProperty return QueryOutputProperty ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -554,6 +573,7 @@ class ConfigureOutputProperty (rq.Request): ) def configure_output_property (self, output, property): + # type: (Display | resource.Resource, int, int) -> ConfigureOutputProperty return ConfigureOutputProperty ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -578,6 +598,7 @@ class ChangeOutputProperty(rq.Request): ) def change_output_property(self, output, property, type, mode, value): + # type: (Display | resource.Resource, int, int, int, int, Sequence[float] | Sequence[str]) -> ChangeOutputProperty return ChangeOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -599,6 +620,7 @@ class DeleteOutputProperty(rq.Request): ) def delete_output_property(self, output, property): + # type: (Display | resource.Resource, int, int) -> DeleteOutputProperty return DeleteOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -634,6 +656,7 @@ class GetOutputProperty(rq.ReplyRequest): ) def get_output_property(self, output, property, type, long_offset, long_length, delete=False, pending=False): + # type: (Display | resource.Resource, int, int, int, int, int, bool, bool) -> GetOutputProperty return GetOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -666,6 +689,7 @@ class CreateMode(rq.ReplyRequest): ) def create_mode(self, mode, name): + # type: (drawable.Window, tuple[int, int, int, int, int, int, int, int, int, int, int, int, int], str) -> CreateMode return CreateMode ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -684,6 +708,7 @@ class DestroyMode(rq.Request): ) def destroy_mode(self, mode): + # type: (Display | resource.Resource, int) -> DestroyMode return DestroyMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -701,6 +726,7 @@ class AddOutputMode(rq.Request): ) def add_output_mode(self, output, mode): + # type: (Display | resource.Resource, int, int) -> AddOutputMode return AddOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -719,6 +745,7 @@ class DeleteOutputMode(rq.Request): ) def delete_output_mode(self, output, mode): + # type: (Display | resource.Resource, int, int) -> DeleteOutputMode return DeleteOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -755,6 +782,7 @@ class GetCrtcInfo(rq.ReplyRequest): ) def get_crtc_info(self, crtc, config_timestamp): + # type: (Display | resource.Resource, int, int) -> GetCrtcInfo return GetCrtcInfo ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -788,6 +816,7 @@ class SetCrtcConfig(rq.ReplyRequest): ) def set_crtc_config(self, crtc, config_timestamp, x, y, mode, rotation, outputs, timestamp=X.CurrentTime): + # type: (Display | resource.Resource, int, int, int, int, int, int, Sequence[int], int) -> SetCrtcConfig return SetCrtcConfig ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -819,6 +848,7 @@ class GetCrtcGammaSize(rq.ReplyRequest): ) def get_crtc_gamma_size(self, crtc): + # type: (Display | resource.Resource, int) -> GetCrtcGammaSize return GetCrtcGammaSize ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -846,6 +876,7 @@ class GetCrtcGamma(rq.ReplyRequest): ) def get_crtc_gamma(self, crtc): + # type: (Display | resource.Resource, int) -> GetCrtcGamma return GetCrtcGamma ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -867,6 +898,7 @@ class SetCrtcGamma(rq.Request): ) def set_crtc_gamma(self, crtc, size, red, green, blue): + # type: (Display | resource.Resource, int, int, Sequence[int], Sequence[int], Sequence[int]) -> SetCrtcGamma return SetCrtcGamma( display=self.display, opcode=self.display.get_extension_major(extname), @@ -906,6 +938,7 @@ class GetScreenResourcesCurrent(rq.ReplyRequest): ) def get_screen_resources_current(self): + # type: (drawable.Window) -> GetScreenResourcesCurrent return GetScreenResourcesCurrent( display=self.display, opcode=self.display.get_extension_major(extname), @@ -927,6 +960,7 @@ class SetCrtcTransform(rq.Request): ) def set_crtc_transform(self, crtc, n_bytes_filter): + # type: (Display | resource.Resource, int, Sequence[int]) -> SetCrtcTransform return SetCrtcTransform( display=self.display, opcode=self.display.get_extension_major(extname), @@ -963,6 +997,7 @@ class GetCrtcTransform(rq.ReplyRequest): ) def get_crtc_transform(self, crtc): + # type: (Display | resource.Resource, int) -> GetCrtcTransform return GetCrtcTransform( display=self.display, opcode=self.display.get_extension_major(extname), @@ -998,6 +1033,7 @@ class GetPanning(rq.ReplyRequest): ) def get_panning(self, crtc): + # type: (Display | resource.Resource, int) -> GetPanning return GetPanning ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1035,6 +1071,7 @@ class SetPanning(rq.ReplyRequest): ) def set_panning(self, crtc, left, top, width, height, track_left, track_top, track_width, track_height, border_left, border_top, border_width, border_height, timestamp=X.CurrentTime): + # type: (Display | resource.Resource, int, int, int, int, int, int, int, int, int, int, int, int, int, int) -> SetPanning return SetPanning ( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1065,6 +1102,7 @@ class SetOutputPrimary(rq.Request): ) def set_output_primary(self, output): + # type: (drawable.Window, int) -> SetOutputPrimary return SetOutputPrimary( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1090,6 +1128,7 @@ class GetOutputPrimary(rq.ReplyRequest): ) def get_output_primary(self): + # type: (drawable.Window) -> GetOutputPrimary return GetOutputPrimary( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1123,6 +1162,7 @@ class GetMonitors(rq.ReplyRequest): def get_monitors(self, is_active=True): + # type: (drawable.Window, bool) -> GetMonitors return GetMonitors( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1141,6 +1181,7 @@ class SetMonitor(rq.Request): def set_monitor(self, monitor_info): + # type: (drawable.Window, tuple[int, bool, bool, Sequence[int], int, int, int, int, int]) -> SetMonitor return SetMonitor( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1160,6 +1201,7 @@ class DeleteMonitor(rq.Request): def delete_monitor(self, name): + # type: (Display | resource.Resource, str) -> DeleteMonitor return DeleteMonitor( display=self.display, opcode=self.display.get_extension_major(extname), @@ -1241,6 +1283,7 @@ class OutputPropertyNotify(rq.Event): # Initialization # def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('display', 'xrandr_query_version', query_version) disp.extension_add_method('window', 'xrandr_select_input', select_input) disp.extension_add_method('window', 'xrandr_get_screen_info', get_screen_info) diff --git a/Xlib/ext/record.py b/Xlib/ext/record.py index 638a5b3..d4c9462 100644 --- a/Xlib/ext/record.py +++ b/Xlib/ext/record.py @@ -21,6 +21,18 @@ from Xlib.protocol import rq +try: + from typing import TYPE_CHECKING, Any, TypeVar +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.protocol import display + from collections.abc import Sequence, Sized, Callable + from Xlib.xobject import resource + _T = TypeVar("_T") + _S = TypeVar("_S", bound=Sized) + extname = 'RECORD' FromServerTime = 0x01 @@ -72,9 +84,11 @@ class RawField(rq.ValueField): structcode = None def pack_value(self, val): + # type: (_S) -> tuple[_S, int, None] return val, len(val), None def parse_binary_value(self, data, display, length, format): + # type: (_T, object, object, object) -> tuple[_T, bytes] return data, '' @@ -94,6 +108,7 @@ class GetVersion(rq.ReplyRequest): rq.Pad(20)) def get_version(self, major, minor): + # type: (Display | resource.Resource, int, int) -> GetVersion return GetVersion( display = self.display, opcode = self.display.get_extension_major(extname), @@ -115,6 +130,7 @@ class CreateContext(rq.Request): rq.List('ranges', Record_Range)) def create_context(self, datum_flags, clients, ranges): + # type: (Display | resource.Resource, int, Sequence[int], Sequence[tuple[tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], bool, bool]]) -> int context = self.display.allocate_resource_id() CreateContext( display = self.display, @@ -140,6 +156,7 @@ class RegisterClients(rq.Request): rq.List('ranges', Record_Range)) def register_clients(self, context, element_header, clients, ranges): + # type: (Display | resource.Resource, int, int, Sequence[int], Sequence[tuple[tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], tuple[int, int], bool, bool]]) -> None RegisterClients( display = self.display, opcode = self.display.get_extension_major(extname), @@ -159,6 +176,7 @@ class UnregisterClients(rq.Request): rq.List('clients', rq.Card32Obj)) def unregister_clients(self, context, clients): + # type: (Display | resource.Resource, int, Sequence[int]) -> None UnregisterClients( display = self.display, opcode = self.display.get_extension_major(extname), @@ -183,6 +201,7 @@ class GetContext(rq.ReplyRequest): rq.List('client_info', Record_ClientInfo)) def get_context(self, context): + # type: (Display | resource.Resource, int) -> GetContext return GetContext( display = self.display, opcode = self.display.get_extension_major(extname), @@ -215,6 +234,7 @@ class EnableContext(rq.ReplyRequest): # See the discussion on ListFonstsWithInfo in request.py def __init__(self, callback, *args, **keys): + # type: (Callable[[rq.DictWrapper | dict[str, object]], Any], display.Display, bool, object, object) -> None self._callback = callback rq.ReplyRequest.__init__(self, *args, **keys) @@ -235,6 +255,7 @@ def _parse_response(self, data): self._display.sent_requests.insert(0, self) def enable_context(self, context, callback): + # type: (Display | resource.Resource, int, Callable[[rq.DictWrapper | dict[str, object]], Any]) -> None EnableContext( callback = callback, display = self.display, @@ -250,6 +271,7 @@ class DisableContext(rq.Request): rq.Card32('context')) # Record_RC def disable_context(self, context): + # type: (Display | resource.Resource, int) -> None DisableContext( display = self.display, opcode = self.display.get_extension_major(extname), @@ -264,6 +286,7 @@ class FreeContext(rq.Request): rq.Card32('context')) # Record_RC def free_context(self, context): + # type: (Display | resource.Resource, int) -> None FreeContext( display = self.display, opcode = self.display.get_extension_major(extname), @@ -272,6 +295,7 @@ def free_context(self, context): def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'record_get_version', get_version) disp.extension_add_method('display', 'record_create_context', create_context) disp.extension_add_method('display', 'record_register_clients', register_clients) diff --git a/Xlib/ext/res.py b/Xlib/ext/res.py index f2c4e9f..2eb088d 100644 --- a/Xlib/ext/res.py +++ b/Xlib/ext/res.py @@ -29,6 +29,14 @@ https://cgit.freedesktop.org/xcb/proto/tree/src/res.xml """ from Xlib.protocol import rq +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.xobject import resource + from collections.abc import Sequence RES_MAJOR_VERSION = 1 RES_MINOR_VERSION = 2 @@ -65,6 +73,7 @@ class QueryVersion(rq.ReplyRequest): def query_version(self, client_major=RES_MAJOR_VERSION, client_minor=RES_MINOR_VERSION): + # type: (Display | resource.Resource, int, int) -> QueryVersion """ Query the protocol version supported by the X server. The client sends the highest supported version to the server and the @@ -98,6 +107,7 @@ class QueryClients(rq.ReplyRequest): def query_clients(self): + # type: (Display | resource.Resource) -> QueryClients """Request the list of all currently connected clients.""" return QueryClients( display=self.display, @@ -126,6 +136,7 @@ class QueryClientResources(rq.ReplyRequest): def query_client_resources(self, client): + # type: (Display | resource.Resource, int) -> QueryClientResources """Request the number of resources owned by a client. The server will return the counts of each type of resource. @@ -153,6 +164,7 @@ class QueryClientPixmapBytes(rq.ReplyRequest): def query_client_pixmap_bytes(self, client): + # type: (Display | resource.Resource, int) -> QueryClientPixmapBytes """Query the pixmap usage of some client. The returned number is a sum of memory usage of each pixmap that can be @@ -169,10 +181,12 @@ class SizeOf(rq.LengthOf): may vary, e.g. List """ def __init__(self, name, size, item_size): + # type: (str | list[str] | tuple[str, ...], int, int) -> None rq.LengthOf.__init__(self, name, size) self.item_size = item_size def parse_value(self, length, display): + # type: (int, object) -> int return length // self.item_size @@ -209,6 +223,7 @@ class QueryClientIds(rq.ReplyRequest): def query_client_ids(self, specs): + # type: (Display | resource.Resource, Sequence[tuple[int, int]]) -> QueryClientIds """Request to identify a given set of clients with some identification method. The request sends a list of specifiers that select clients and @@ -262,6 +277,7 @@ class QueryResourceBytes(rq.ReplyRequest): def query_resource_bytes(self, client, specs): + # type: (Display | resource.Resource, int, Sequence[tuple[int, int]]) -> QueryResourceBytes """Query the sizes of resources from X server. The request sends a list of specifiers that selects resources for size @@ -276,6 +292,7 @@ def query_resource_bytes(self, client, specs): def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method("display", "res_query_version", query_version) disp.extension_add_method("display", "res_query_clients", query_clients) disp.extension_add_method("display", "res_query_client_resources", diff --git a/Xlib/ext/screensaver.py b/Xlib/ext/screensaver.py index 12f4325..9bde3d7 100644 --- a/Xlib/ext/screensaver.py +++ b/Xlib/ext/screensaver.py @@ -33,6 +33,19 @@ from Xlib import X from Xlib.protocol import rq, structs +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from Xlib.error import XError + from Xlib.protocol import request + from Xlib.display import Display + from Xlib.xobject import drawable, resource + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[rq.Request]], _T] + extname = 'MIT-SCREEN-SAVER' # Event members @@ -70,6 +83,7 @@ class QueryVersion(rq.ReplyRequest): ) def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -98,6 +112,7 @@ class QueryInfo(rq.ReplyRequest): ) def query_info(self): + # type: (drawable.Drawable) -> QueryInfo return QueryInfo(display=self.display, opcode=self.display.get_extension_major(extname), drawable=self, @@ -114,6 +129,7 @@ class SelectInput(rq.Request): ) def select_input(self, mask): + # type: (drawable.Drawable, int) -> SelectInput return SelectInput(display=self.display, opcode=self.display.get_extension_major(extname), drawable=self, @@ -144,6 +160,7 @@ def set_attributes(self, x, y, width, height, border_width, visual = X.CopyFromParent, onerror = None, **keys): + # type: (drawable.Drawable, int, int, int, int, int, int, int, int, _ErrorHandler[object] | None, object) -> SetAttributes return SetAttributes(display=self.display, onerror = onerror, opcode=self.display.get_extension_major(extname), @@ -168,6 +185,7 @@ class UnsetAttributes(rq.Request): ) def unset_attributes(self, onerror = None): + # type: (drawable.Drawable, _ErrorHandler[object] | None) -> UnsetAttributes return UnsetAttributes(display=self.display, onerror = onerror, opcode=self.display.get_extension_major(extname), @@ -189,6 +207,7 @@ class Notify(rq.Event): ) def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('display', 'screensaver_query_version', query_version) disp.extension_add_method('drawable', 'screensaver_query_info', query_info) disp.extension_add_method('drawable', 'screensaver_select_input', select_input) diff --git a/Xlib/ext/security.py b/Xlib/ext/security.py index a821017..c0ed406 100644 --- a/Xlib/ext/security.py +++ b/Xlib/ext/security.py @@ -27,6 +27,13 @@ from Xlib.protocol import rq +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.xobject import resource extname = 'SECURITY' @@ -58,6 +65,7 @@ class QueryVersion(rq.ReplyRequest): def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -91,6 +99,7 @@ class SecurityGenerateAuthorization(rq.ReplyRequest): def generate_authorization(self, auth_proto, auth_data=b'', timeout=None, trust_level=None, group=None, event_mask=None): + # type: (Display | resource.Resource, str, bytes | bytearray, int | None, int | None, int | None, int | None) -> SecurityGenerateAuthorization value_mask = 0 values = [] if timeout is not None: @@ -122,12 +131,14 @@ class SecurityRevokeAuthorization(rq.Request): def revoke_authorization(self, authid): + # type: (Display | resource.Resource, int) -> SecurityRevokeAuthorization return SecurityRevokeAuthorization(display=self.display, opcode=self.display.get_extension_major(extname), authid=authid) def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'security_query_version', query_version) diff --git a/Xlib/ext/shape.py b/Xlib/ext/shape.py index 05a517a..3178120 100644 --- a/Xlib/ext/shape.py +++ b/Xlib/ext/shape.py @@ -3,6 +3,15 @@ from Xlib.protocol import rq, structs +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.protocol import request + from Xlib.xobject import resource, drawable + from collections.abc import Sequence extname = 'SHAPE' @@ -23,6 +32,7 @@ class SK: class KIND(rq.Set): def __init__(self, name): + # type: (str) -> None super(KIND, self).__init__(name, 1, values=(SK.Bounding, SK.Clip, @@ -201,6 +211,7 @@ class Event: Notify = 0 def combine(self, operation, destination_kind, source_kind, x_offset, y_offset): + # type: (drawable.Window, int, int, int, int, int) -> None Combine( display=self.display, opcode=self.display.get_extension_major(extname), @@ -213,6 +224,7 @@ def combine(self, operation, destination_kind, source_kind, x_offset, y_offset): ) def get_rectangles(self, source_kind): + # type: (drawable.Window, int) -> GetRectangles return GetRectangles( display=self.display, opcode=self.display.get_extension_major(extname), @@ -221,6 +233,7 @@ def get_rectangles(self, source_kind): ) def input_selected(self, ): + # type: (drawable.Window) -> InputSelected return InputSelected( display=self.display, opcode=self.display.get_extension_major(extname), @@ -228,6 +241,7 @@ def input_selected(self, ): ) def mask(self, operation, destination_kind, x_offset, y_offset, source_bitmap): + # type: (drawable.Window, int, int, int, int, int) -> None Mask( display=self.display, opcode=self.display.get_extension_major(extname), @@ -240,6 +254,7 @@ def mask(self, operation, destination_kind, x_offset, y_offset, source_bitmap): ) def offset(self, destination_kind, x_offset, y_offset): + # type: (drawable.Window, int, int, int) -> None Offset( display=self.display, opcode=self.display.get_extension_major(extname), @@ -250,6 +265,7 @@ def offset(self, destination_kind, x_offset, y_offset): ) def query_extents(self, ): + # type: (drawable.Window) -> QueryExtents return QueryExtents( display=self.display, opcode=self.display.get_extension_major(extname), @@ -257,12 +273,14 @@ def query_extents(self, ): ) def query_version(self, ): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion( display=self.display, opcode=self.display.get_extension_major(extname), ) def rectangles(self, operation, destination_kind, ordering, x_offset, y_offset, rectangles): + # type: (drawable.Window, int, int, int, int, int, Sequence[tuple[int, int, int, int]]) -> None Rectangles( display=self.display, opcode=self.display.get_extension_major(extname), @@ -276,6 +294,7 @@ def rectangles(self, operation, destination_kind, ordering, x_offset, y_offset, ) def select_input(self, enable): + # type: (drawable.Window, int) -> None SelectInput( display=self.display, opcode=self.display.get_extension_major(extname), @@ -284,6 +303,7 @@ def select_input(self, enable): ) def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('window', 'shape_combine', combine) disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles) disp.extension_add_method('window', 'shape_input_selected', input_selected) diff --git a/Xlib/ext/xfixes.py b/Xlib/ext/xfixes.py index 8b3c35f..fb7822d 100644 --- a/Xlib/ext/xfixes.py +++ b/Xlib/ext/xfixes.py @@ -25,7 +25,9 @@ ShowCursor requests and SelectionNotify events are provided. ''' -from Xlib.protocol import rq +from Xlib.protocol import rq, request +from Xlib.display import Display +from Xlib.xobject import resource, drawable extname = 'XFIXES' @@ -60,6 +62,7 @@ class QueryVersion(rq.ReplyRequest): def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=4, @@ -74,6 +77,7 @@ class HideCursor(rq.Request): ) def hide_cursor(self): + # type: (drawable.Window) -> None HideCursor(display=self.display, opcode=self.display.get_extension_major(extname), window=self) @@ -88,6 +92,7 @@ class ShowCursor(rq.Request): def show_cursor(self): + # type: (drawable.Window) -> None ShowCursor(display=self.display, opcode=self.display.get_extension_major(extname), window=self) @@ -102,6 +107,7 @@ class SelectSelectionInput(rq.Request): ) def select_selection_input(self, window, selection, mask): + # type: (Display | resource.Resource, int, int, int) -> SelectSelectionInput return SelectSelectionInput(opcode=self.display.get_extension_major(extname), display=self.display, window=window, @@ -143,6 +149,7 @@ class SelectCursorInput(rq.Request): ) def select_cursor_input(self, window, mask): + # type: (Display | resource.Resource, int, int) -> SelectCursorInput return SelectCursorInput(opcode=self.display.get_extension_major(extname), display=self.display, window=window, @@ -171,6 +178,7 @@ class GetCursorImage(rq.ReplyRequest): ) def get_cursor_image(self, window): + # type: (Display | resource.Resource, object) -> GetCursorImage return GetCursorImage(opcode=self.display.get_extension_major(extname), display=self.display, ) @@ -187,6 +195,7 @@ class DisplayCursorNotify(rq.Event): def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('display', 'xfixes_select_selection_input', select_selection_input) disp.extension_add_method('display', 'xfixes_query_version', query_version) disp.extension_add_method('window', 'xfixes_hide_cursor', hide_cursor) diff --git a/Xlib/ext/xinerama.py b/Xlib/ext/xinerama.py index dcb78b8..1430a2c 100644 --- a/Xlib/ext/xinerama.py +++ b/Xlib/ext/xinerama.py @@ -36,6 +36,8 @@ from Xlib.protocol import rq, structs +from Xlib.display import Display +from Xlib.xobject import resource, drawable extname = 'XINERAMA' @@ -61,6 +63,7 @@ class QueryVersion(rq.ReplyRequest): ) def query_version(self): + # type: (Display | resource.Resource) -> QueryVersion return QueryVersion(display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, @@ -84,6 +87,7 @@ class GetState(rq.ReplyRequest): ) def get_state(self): + # type: (drawable.Window) -> GetState return GetState(display=self.display, opcode=self.display.get_extension_major(extname), window=self.id, @@ -107,6 +111,7 @@ class GetScreenCount(rq.ReplyRequest): ) def get_screen_count(self): + # type: (drawable.Window) -> GetScreenCount return GetScreenCount(display=self.display, opcode=self.display.get_extension_major(extname), window=self.id, @@ -134,6 +139,7 @@ class GetScreenSize(rq.ReplyRequest): ) def get_screen_size(self, screen_no): + # type: (drawable.Window, int) -> GetScreenSize """Returns the size of the given screen number""" return GetScreenSize(display=self.display, opcode=self.display.get_extension_major(extname), @@ -160,6 +166,7 @@ class IsActive(rq.ReplyRequest): ) def is_active(self): + # type: (Display | resource.Resource) -> int r = IsActive(display=self.display, opcode=self.display.get_extension_major(extname), ) @@ -184,6 +191,7 @@ class QueryScreens(rq.ReplyRequest): ) def query_screens(self): + # type: (Display | resource.Resource) -> QueryScreens # Hmm. This one needs to read the screen data from the socket. Ooops... return QueryScreens(display=self.display, opcode=self.display.get_extension_major(extname), @@ -208,11 +216,13 @@ class GetInfo(rq.ReplyRequest): ) def get_info(self, visual): + # type: (Display | resource.Resource, int) -> None r = GetInfo(display=self.display, opcode=self.display.get_extension_major(extname), visual=visual) def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'xinerama_query_version', query_version) disp.extension_add_method('window', 'xinerama_get_state', get_state) disp.extension_add_method('window', 'xinerama_get_screen_count', get_screen_count) diff --git a/Xlib/ext/xinput.py b/Xlib/ext/xinput.py index f921806..ed06566 100644 --- a/Xlib/ext/xinput.py +++ b/Xlib/ext/xinput.py @@ -34,6 +34,22 @@ from Xlib.protocol import rq from Xlib import X +try: + from typing import TYPE_CHECKING, Union, Any, SupportsFloat, TypeVar +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from Xlib.display import Display + from Xlib.protocol import display + from Xlib.protocol import request + from collections.abc import Sequence, Iterable + from Xlib.xobject import drawable, resource + from mmap import mmap + from typing_extensions import SupportsIndex + from _typeshed import ReadableBuffer + _T = TypeVar("_T") + _SliceableBuffer = Union[bytes, bytearray, memoryview, array.array[Any], mmap] + _Floatable = Union[SupportsFloat, SupportsIndex, str, ReadableBuffer] extname = 'XInputExtension' @@ -163,9 +179,11 @@ class FP1616(rq.Int32): def check_value(self, value): + # type: (float) -> int return int(value * 65536.0) def parse_value(self, value, display): + # type: (_Floatable, object) -> float return float(value) / float(1 << 16) class FP3232(rq.ValueField): @@ -173,9 +191,11 @@ class FP3232(rq.ValueField): structvalues = 2 def check_value(self, value): + # type: (_T) -> _T return value def parse_value(self, value, display): + # type: (tuple[_Floatable, _Floatable], object) -> float integral, frac = value ret = float(integral) # optimised math.ldexp(float(frac), -32) @@ -202,6 +222,7 @@ class XIQueryVersion(rq.ReplyRequest): def query_version(self): + # type: (Display | resource.Resource) -> XIQueryVersion return XIQueryVersion( display=self.display, opcode=self.display.get_extension_major(extname), @@ -212,11 +233,13 @@ def query_version(self): class Mask(rq.List): def __init__(self, name): + # type: (str) -> None rq.List.__init__(self, name, rq.Card32, pad=0) def pack_value(self, val): + # type: (int | Iterable[int]) -> tuple[bytes, int, None] - mask_seq = array.array(rq.struct_to_array_codes['L']) + mask_seq = array.array(rq.struct_to_array_codes['L']) # type: array.array[int] if isinstance(val, integer_types): # We need to build a "binary mask" that (as far as I can tell) is @@ -259,6 +282,7 @@ class XISelectEvents(rq.Request): ) def select_events(self, event_masks): + # type: (drawable.Window, Sequence[tuple[int, Sequence[int]]]) -> XISelectEvents ''' select_events(event_masks) @@ -284,6 +308,7 @@ def select_events(self, event_masks): class ButtonMask(object): def __init__(self, value, length): + # type: (int, int) -> None self._value = value self._length = length @@ -291,6 +316,7 @@ def __len__(self): return self._length def __getitem__(self, key): + # type: (int) -> int return self._value & (1 << key) def __str__(self): @@ -305,9 +331,11 @@ class ButtonState(rq.ValueField): structcode = None def __init__(self, name): + # type: (str) -> None rq.ValueField.__init__(self, name) def parse_binary_value(self, data, display, length, fmt): + # type: (_SliceableBuffer, object, int | None, object) -> tuple[ButtonMask, _SliceableBuffer] # Mask: bitfield of button states. mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) mask_data = data[:mask_len] @@ -382,6 +410,7 @@ class ClassInfoClass(object): structcode = None def parse_binary(self, data, display): + # type: (_SliceableBuffer, display.Display | None) -> tuple[rq.DictWrapper, _SliceableBuffer] class_type, length = struct.unpack('=HH', data[:4]) class_struct = INFO_CLASSES.get(class_type, AnyInfo) class_data, _ = class_struct.parse_binary(data, display) @@ -422,6 +451,7 @@ class XIQueryDevice(rq.ReplyRequest): ) def query_device(self, deviceid): + # type: (Display | resource.Resource, int) -> XIQueryDevice return XIQueryDevice( display=self.display, opcode=self.display.get_extension_major(extname), @@ -448,6 +478,7 @@ class XIListProperties(rq.ReplyRequest): ) def list_device_properties(self, deviceid): + # type: (Display | resource.Resource, int) -> XIListProperties return XIListProperties( display=self.display, opcode=self.display.get_extension_major(extname), @@ -482,6 +513,7 @@ class XIGetProperty(rq.ReplyRequest): ) def get_device_property(self, deviceid, property, type, offset, length, delete=False): + # type: (Display | resource.Resource, int, int, int, int, int, int) -> XIGetProperty return XIGetProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -508,6 +540,7 @@ class XIChangeProperty(rq.Request): ) def change_device_property(self, deviceid, property, type, mode, value): + # type: (Display | resource.Resource, int, int, int, int, Sequence[float] | Sequence[str]) -> XIChangeProperty return XIChangeProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -529,6 +562,7 @@ class XIDeleteProperty(rq.Request): ) def delete_device_property(self, deviceid, property): + # type: (Display | resource.Resource, int, int) -> XIDeleteProperty return XIDeleteProperty( display=self.display, opcode=self.display.get_extension_major(extname), @@ -563,6 +597,7 @@ class XIGrabDevice(rq.ReplyRequest): ) def grab_device(self, deviceid, time, grab_mode, paired_device_mode, owner_events, event_mask): + # type: (drawable.Window, int, int, int, int, bool, Sequence[int]) -> XIGrabDevice return XIGrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), @@ -587,6 +622,7 @@ class XIUngrabDevice(rq.Request): ) def ungrab_device(self, deviceid, time): + # type: (Display | resource.Resource, int, int) -> XIUngrabDevice return XIUngrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), @@ -629,6 +665,7 @@ class XIPassiveGrabDevice(rq.ReplyRequest): def passive_grab_device(self, deviceid, time, detail, grab_type, grab_mode, paired_device_mode, owner_events, event_mask, modifiers): + # type: (drawable.Window, int, int, int, int, int, int, bool, Sequence[int], Sequence[int]) -> XIPassiveGrabDevice return XIPassiveGrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), @@ -648,6 +685,7 @@ def passive_grab_device(self, deviceid, time, detail, def grab_keycode(self, deviceid, time, keycode, grab_mode, paired_device_mode, owner_events, event_mask, modifiers): + # type: (drawable.Window, int, int, int, int, int, bool, Sequence[int], Sequence[int]) -> XIPassiveGrabDevice return passive_grab_device(self, deviceid, time, keycode, GrabtypeKeycode, grab_mode, paired_device_mode, @@ -671,6 +709,7 @@ class XIPassiveUngrabDevice(rq.Request): ) def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): + # type: (drawable.Window, int, int, int, Sequence[int]) -> XIPassiveUngrabDevice return XIPassiveUngrabDevice( display=self.display, opcode=self.display.get_extension_major(extname), @@ -682,6 +721,7 @@ def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): ) def ungrab_keycode(self, deviceid, keycode, modifiers): + # type: (drawable.Window, int, int, Sequence[int]) -> XIPassiveUngrabDevice return passive_ungrab_device(self, deviceid, keycode, GrabtypeKeycode, modifiers) @@ -758,6 +798,7 @@ def ungrab_keycode(self, deviceid, keycode, modifiers): ) def init(disp, info): + # type: (Display, request.QueryExtension) -> None disp.extension_add_method('display', 'xinput_query_version', query_version) disp.extension_add_method('window', 'xinput_select_events', select_events) disp.extension_add_method('display', 'xinput_query_device', query_device) diff --git a/Xlib/ext/xtest.py b/Xlib/ext/xtest.py index 602df2a..0a8901f 100644 --- a/Xlib/ext/xtest.py +++ b/Xlib/ext/xtest.py @@ -21,6 +21,8 @@ from Xlib import X from Xlib.protocol import rq +from Xlib.display import Display +from Xlib.xobject import resource extname = 'XTEST' @@ -44,6 +46,7 @@ class GetVersion(rq.ReplyRequest): ) def get_version(self, major, minor): + # type: (Display | resource.Resource, int, int) -> GetVersion return GetVersion(display = self.display, opcode = self.display.get_extension_major(extname), major_version = major, @@ -65,6 +68,7 @@ class CompareCursor(rq.ReplyRequest): ) def compare_cursor(self, cursor): + # type: (Display | resource.Resource, int) -> int r = CompareCursor(display = self.display, opcode = self.display.get_extension_major(extname), window = self.id, @@ -92,6 +96,7 @@ class FakeInput(rq.Request): def fake_input(self, event_type, detail = 0, time = X.CurrentTime, root = X.NONE, x = 0, y = 0): + # type: (Display | resource.Resource, int, int, int, int, int, int) -> None FakeInput(display = self.display, opcode = self.display.get_extension_major(extname), @@ -111,11 +116,13 @@ class GrabControl(rq.Request): ) def grab_control(self, impervious): + # type: (Display | resource.Resource, bool) -> None GrabControl(display = self.display, opcode = self.display.get_extension_major(extname), impervious = impervious) def init(disp, info): + # type: (Display, object) -> None disp.extension_add_method('display', 'xtest_get_version', get_version) disp.extension_add_method('window', 'xtest_compare_cursor', compare_cursor) disp.extension_add_method('display', 'xtest_fake_input', fake_input) diff --git a/Xlib/protocol/display.py b/Xlib/protocol/display.py index 0d910da..558625e 100644 --- a/Xlib/protocol/display.py +++ b/Xlib/protocol/display.py @@ -30,7 +30,7 @@ import sys # Python 2/3 compatibility. -from six import PY3, byte2int, indexbytes +from six import byte2int, indexbytes # Xlib modules from .. import error @@ -42,11 +42,37 @@ from . import rq from . import event -if PY3: +try: + from typing import TYPE_CHECKING, overload, Union, Any, Optional, TypeVar +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from typing_extensions import Literal + from array import array + from mmap import mmap + from collections.abc import Callable + from Xlib.display import _ResourceBaseClass + from Xlib.xobject import cursor, colormap, fontable, drawable, resource + from pickle import PickleBuffer + from ctypes import _CData + _T = TypeVar("_T") + _BufferWithLen = Union[bytes, bytearray, memoryview, array[Any], mmap, _CData, PickleBuffer] + _ErrorHandler = Callable[[error.XError, Optional[rq.Request]], _T] + +if sys.version_info[0] == 3: class bytesview(object): - + if TYPE_CHECKING: + @overload + def __init__(self, data, offset, size): + # type: (bytes | bytesview, int, int) -> None + pass + @overload + def __init__(self, data, offset=0, size=None): + # type: (_BufferWithLen, int, None) -> None + pass def __init__(self, data, offset=0, size=None): + # type: (_BufferWithLen | bytesview, int, int | None) -> None if size is None: size = len(data)-offset if isinstance(data, bytes): @@ -55,12 +81,26 @@ def __init__(self, data, offset=0, size=None): view = data.view else: raise TypeError('unsupported type: {}'.format(type(data))) - self.view = view[offset:offset+size] + self.view = view[offset:offset+size] # type: memoryview def __len__(self): return len(self.view) + if TYPE_CHECKING: + def __contains__(self, other): + # type: (object) -> bool + pass + + @overload + def __getitem__(self, key): + # type: (int) -> int + pass + @overload + def __getitem__(self, key): + # type: (slice) -> bytes + pass def __getitem__(self, key): + # type: (slice | int) -> bytes | int if isinstance(key, slice): return bytes(self.view[key]) return self.view[key] @@ -76,11 +116,12 @@ def bytesview(data, offset=0, size=None): class Display(object): - extension_major_opcodes = {} - error_classes = error.xerror_class.copy() - event_classes = event.event_class.copy() + extension_major_opcodes = {} # type: dict[str, int] + error_classes = error.xerror_class.copy() # type: dict[int, type[error.XError]] + event_classes = event.event_class.copy() # type: dict[int, type[rq.Event] | dict[int, type[rq.Event]]] def __init__(self, display = None): + # type: (str | None) -> None name, protocol, host, displayno, screenno = connect.get_display(display) self.display_name = name @@ -97,17 +138,17 @@ def __init__(self, display = None): # Socket error indicator, set when the socket is closed # in one way or another self.socket_error_lock = lock.allocate_lock() - self.socket_error = None + self.socket_error = None # type: Exception | None # Event queue self.event_queue_read_lock = lock.allocate_lock() self.event_queue_write_lock = lock.allocate_lock() - self.event_queue = [] + self.event_queue = [] # type: list[rq.Event] # Unsent request queue and sequence number counter self.request_queue_lock = lock.allocate_lock() self.request_serial = 1 - self.request_queue = [] + self.request_queue = [] # type: list[tuple[rq.Request | rq.ReplyRequest | ConnectionSetupRequest, bool]] # Send-and-receive loop, see function send_and_receive # for a detailed explanation @@ -127,26 +168,26 @@ def __init__(self, display = None): self.recv_buffer_size = int(buffer_size) # Data used by the send-and-receive loop - self.sent_requests = [] + self.sent_requests = [] # type: list[rq.Request | rq.ReplyRequest | ConnectionSetupRequest] self.recv_packet_len = 0 self.data_send = b'' - self.data_recv = b'' + self.data_recv = b'' # type: bytes | bytesview self.data_sent_bytes = 0 # Resource ID structures self.resource_id_lock = lock.allocate_lock() - self.resource_ids = {} + self.resource_ids = {} # type: dict[int, None] self.last_resource_id = 0 # Use an default error handler, one which just prints the error - self.error_handler = None + self.error_handler = None # type: _ErrorHandler[object] | None # Right, now we're all set up for the connection setup # request with the server. # Figure out which endianness the hardware uses - self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] + self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] # type: bool if self.big_endian: order = 0x42 @@ -259,10 +300,12 @@ def close(self): self.close_internal('client') def set_error_handler(self, handler): + # type: (_ErrorHandler[object] | None) -> None self.error_handler = handler def allocate_resource_id(self): + # type: () -> int """id = d.allocate_resource_id() Allocate a new X resource id number ID. @@ -287,6 +330,7 @@ def allocate_resource_id(self): self.resource_id_lock.release() def free_resource_id(self, rid): + # type: (int) -> None """d.free_resource_id(rid) Free resource id RID. Attempts to free a resource id which @@ -308,9 +352,53 @@ def free_resource_id(self, rid): finally: self.resource_id_lock.release() - - + if TYPE_CHECKING: + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['resource'], object) -> type[resource.Resource] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['drawable'], object) -> type[drawable.Drawable] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['window'], object) -> type[drawable.Window] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['pixmap'], object) -> type[drawable.Pixmap] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['fontable'], object) -> type[fontable.Fontable] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['font'], object) -> type[fontable.Font] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['gc'], object) -> type[fontable.GC] + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (Literal['colormap'], object) -> type[colormap.Colormap] + pass + @overload + def get_resource_class(self, class_name, default): + # type: (Literal['cursor'], object) -> type[cursor.Cursor] + pass + @overload + def get_resource_class(self, class_name, default): + # type: (str, _T) -> type[_ResourceBaseClass] | _T + pass + @overload + def get_resource_class(self, class_name, default = None): + # type: (str, None) -> type[_ResourceBaseClass] | None + pass def get_resource_class(self, class_name, default = None): + # type: (str, _T | None) -> _T | None """class = d.get_resource_class(class_name, default = None) Return the class to be used for X resource objects of type @@ -320,12 +408,15 @@ def get_resource_class(self, class_name, default = None): return self.resource_classes.get(class_name, default) def set_extension_major(self, extname, major): + # type: (str, int) -> None self.extension_major_opcodes[extname] = major def get_extension_major(self, extname): + # type: (str) -> int return self.extension_major_opcodes[extname] def add_extension_event(self, code, evt, subcode=None): + # type: (int, type[rq.Event], int | None) -> None if subcode == None: self.event_classes[code] = evt else: @@ -335,6 +426,7 @@ def add_extension_event(self, code, evt, subcode=None): self.event_classes[code][subcode] = evt def add_extension_error(self, code, err): + # type: (int, type[error.XError]) -> None self.error_classes[code] = err @@ -351,6 +443,7 @@ def check_for_error(self): raise err def send_request(self, request, wait_for_response): + # type: (rq.Request | rq.ReplyRequest | ConnectionSetupRequest, bool) -> None if self.socket_error: raise self.socket_error @@ -368,6 +461,7 @@ def send_request(self, request, wait_for_response): # self.flush() def close_internal(self, whom): + # type: (object) -> None # Clear out data structures self.request_queue = None self.sent_requests = None @@ -385,6 +479,7 @@ def close_internal(self, whom): def send_and_recv(self, flush = False, event = False, request = None, recv = False): + # type: (bool, bool, int | None, bool) -> None """send_and_recv(flush = None, event = None, request = None, recv = None) Perform I/O, or wait for some other thread to do it for us. @@ -675,6 +770,7 @@ def send_and_recv(self, flush = False, event = False, request = None, recv = Fal def parse_response(self, request): + # type: (int) -> bool """Internal method. Parse data received from server. If REQUEST is not None @@ -731,6 +827,7 @@ def parse_response(self, request): def parse_error_response(self, request): + # type: (int) -> bool # Code is second byte code = indexbytes(self.data_recv, 1) @@ -776,10 +873,12 @@ def parse_error_response(self, request): def default_error_handler(self, err): + # type: (object) -> None sys.stderr.write('X protocol error:\n%s\n' % err) def parse_request_response(self, request): + # type: (int) -> bool req = self.get_waiting_replyrequest() # Sequence number is always data[2:4] @@ -811,6 +910,7 @@ def parse_request_response(self, request): def parse_event_response(self, etype): + # type: (int) -> None # Skip bit 8, that is set if this event came from an SendEvent etype = etype & 0x7f @@ -866,6 +966,7 @@ def parse_event_response(self, etype): def get_waiting_request(self, sno): + # type: (int) -> rq.ReplyRequest | ConnectionSetupRequest | None if not self.sent_requests: return None @@ -1060,8 +1161,9 @@ class ConnectionSetupRequest(rq.GetAttrData): def __init__(self, display, *args, **keys): + # type: (Display, object, object) -> None self._binary = self._request.to_binary(*args, **keys) - self._data = None + self._data = None # type: dict[str, object] # Don't bother about locking, since no other threads have # access to the display yet diff --git a/Xlib/protocol/request.py b/Xlib/protocol/request.py index b431e13..b6c0dba 100644 --- a/Xlib/protocol/request.py +++ b/Xlib/protocol/request.py @@ -26,6 +26,7 @@ # Xlib.protocol modules from . import rq from . import structs +from . import display class CreateWindow(rq.Request): @@ -786,6 +787,7 @@ class ListFontsWithInfo(rq.ReplyRequest): # Bastards. def __init__(self, *args, **keys): + # type: (display.Display, bool, object, object) -> None self._fonts = [] rq.ReplyRequest.__init__(self, *args, **keys) diff --git a/Xlib/protocol/rq.py b/Xlib/protocol/rq.py index 8bc8205..759082f 100644 --- a/Xlib/protocol/rq.py +++ b/Xlib/protocol/rq.py @@ -26,18 +26,39 @@ from array import array # Python 2/3 compatibility. -from six import PY3, binary_type, byte2int, indexbytes, iterbytes +from six import binary_type, byte2int, indexbytes, iterbytes # Xlib modules from .. import X from ..support import lock +try: + from typing import TYPE_CHECKING, Any, TypeVar, Union, Optional, SupportsInt, overload +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from ..display import _BaseDisplay + from mmap import mmap + from collections.abc import Iterable, Sequence, Callable + from typing_extensions import Literal, SupportsIndex, LiteralString + from Xlib.error import XError + from Xlib.protocol import display + from pickle import PickleBuffer + from ctypes import _CData + from Xlib.ext.xinput import ClassInfoClass + from Xlib.display import _ResourceBaseClass + _T = TypeVar("_T") + _SliceableBuffer = Union[bytes, bytearray, memoryview, array[Any], mmap] + _IntNew = Union[str, _SliceableBuffer, _CData, PickleBuffer, SupportsInt, SupportsIndex] + _ErrorHandler = Callable[[XError, Optional["Request"]], _T] def decode_string(bs): + # type: (bytes | bytearray) -> str return bs.decode('latin1') -if PY3: +if sys.version_info[0] == 3: def encode_array(a): + # type: (array[Any] | memoryview) -> bytes return a.tobytes() else: def encode_array(a): @@ -59,8 +80,8 @@ class BadDataError(Exception): pass # # Bleah. -array_unsigned_codes = { } -struct_to_array_codes = { } +array_unsigned_codes = { } # type: dict[int, LiteralString] +struct_to_array_codes = { } # type: dict[str, LiteralString] for c in 'bhil': size = array(c).itemsize @@ -109,14 +130,14 @@ class Field(object): f.parse_binary_value() instead. See its documentation string for details. """ - name = None - default = None + name = None # type: str + default = None # type: int | None - structcode = None + structcode = None # type: str | None structvalues = 0 - check_value = None - parse_value = None + check_value = None # type: Callable[[Any], Any] | None + parse_value = None # type: Callable[[Any, Any], Any] | None keyword_args = False @@ -124,6 +145,7 @@ def __init__(self): pass def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display | None, int | None, int) -> tuple[Any, _SliceableBuffer] """value, remaindata = f.parse_binary_value(data, display, length, format) Decode a value for this field from the binary string DATA. @@ -143,6 +165,7 @@ def parse_binary_value(self, data, display, length, format): class Pad(Field): def __init__(self, size): + # type: (int) -> None self.size = size self.value = b'\0' * size self.structcode = '{0}x'.format(size) @@ -151,6 +174,7 @@ def __init__(self, size): class ConstantField(Field): def __init__(self, value): + # type: (int) -> None self.value = value @@ -182,9 +206,10 @@ class LengthField(Field): """ structcode = 'L' structvalues = 1 - other_fields = None + other_fields = None # type: Sequence[str] | None def calc_length(self, length): + # type: (int) -> int """newlen = lf.calc_length(length) Return a new length NEWLEN based on the provided LENGTH. @@ -201,6 +226,7 @@ class RequestLength(TotalLengthField): structvalues = 1 def calc_length(self, length): + # type: (int) -> int return length // 4 class ReplyLength(TotalLengthField): @@ -208,11 +234,13 @@ class ReplyLength(TotalLengthField): structvalues = 1 def calc_length(self, length): + # type: (int) -> int return (length - 32) // 4 class LengthOf(LengthField): def __init__(self, name, size): + # type: (str | list[str] | tuple[str, ...], int) -> None if isinstance(name, (list, tuple)): self.name = name[0] self.other_fields = name[1:] @@ -226,12 +254,15 @@ class OddLength(LengthField): structvalues = 1 def __init__(self, name): + # type: (str) -> None self.name = name def calc_length(self, length): + # type: (int) -> int return length % 2 def parse_value(self, value, display): + # type: (int, object) -> Literal['even', 'odd'] if value == 0: return 'even' else: @@ -249,6 +280,7 @@ class FormatField(Field): structvalues = 1 def __init__(self, name, size): + # type: (str, int) -> None self.name = name self.structcode = unsigned_codes[size] @@ -257,6 +289,7 @@ def __init__(self, name, size): class ValueField(Field): def __init__(self, name, default = None): + # type: (str, int | None) -> None self.name = name self.default = default @@ -291,16 +324,28 @@ class Resource(Card32): class_name = 'resource' def __init__(self, name, codes = (), default = None): + # type: (str, tuple[int, ...], int | None) -> None Card32.__init__(self, name, default) self.codes = codes + if TYPE_CHECKING: + @overload + def check_value(self, value): + # type: (Callable[[], _T]) -> _T + pass + @overload + def check_value(self, value): + # type: (_T) -> _T + pass def check_value(self, value): + # type: (Callable[[], _T] | _T) -> _T if hasattr(value, self.cast_function): return getattr(value, self.cast_function)() else: return value def parse_value(self, value, display): + # type: (int, _BaseDisplay | None) -> int | _ResourceBaseClass # if not display: # return value if value in self.codes: @@ -350,17 +395,20 @@ class Bool(ValueField): structcode = 'B' def check_value(self, value): + # type: (object) -> bool return not not value class Set(ValueField): structvalues = 1 def __init__(self, name, size, values, default = None): + # type: (str, int, Sequence[object], int | None) -> None ValueField.__init__(self, name, default) self.structcode = unsigned_codes[size] self.values = values def check_value(self, val): + # type: (_T) -> _T if val not in self.values: raise ValueError('field %s: argument %s not in %s' % (self.name, val, self.values)) @@ -369,6 +417,7 @@ def check_value(self, val): class Gravity(Set): def __init__(self, name): + # type: (str) -> None Set.__init__(self, name, 1, (X.ForgetGravity, X.StaticGravity, X.NorthWestGravity, X.NorthGravity, X.NorthEastGravity, X.WestGravity, @@ -381,6 +430,7 @@ class FixedBinary(ValueField): structvalues = 1 def __init__(self, name, size): + # type: (str, int) -> None ValueField.__init__(self, name) self.structcode = '{0}s'.format(size) @@ -389,10 +439,12 @@ class Binary(ValueField): structcode = None def __init__(self, name, pad = 1): + # type: (str, int) -> None ValueField.__init__(self, name) self.pad = pad def pack_value(self, val): + # type: (bytes | bytearray) -> tuple[bytes | bytearray, int, None] val_bytes = val slen = len(val_bytes) @@ -401,7 +453,17 @@ def pack_value(self, val): else: return val_bytes, slen, None + if TYPE_CHECKING: + @overload + def parse_binary_value(self, data, display, length, format): + # type: (_T, object, None, object) -> tuple[_T, Literal[b'']] + pass + @overload + def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, int, object) -> tuple[_SliceableBuffer, _SliceableBuffer] + pass def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, int | None, object) -> tuple[_SliceableBuffer, _SliceableBuffer] if length is None: return data, b'' @@ -417,10 +479,12 @@ class String8(ValueField): structcode = None def __init__(self, name, pad = 1): + # type: (str, int) -> None ValueField.__init__(self, name) self.pad = pad def pack_value(self, val): + # type: (bytes | str) -> tuple[bytes, int, None] if isinstance(val, bytes): val_bytes = val else: @@ -432,7 +496,17 @@ def pack_value(self, val): else: return val_bytes, slen, None + if TYPE_CHECKING: + @overload + def parse_binary_value(self, data, display, length, format): + # type: (bytes | bytearray, object, None, object) -> tuple[str, Literal[b'']] + pass + @overload + def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, int, object) -> tuple[str, _SliceableBuffer] + pass def parse_binary_value(self, data, display, length, format): + # type: (bytes | bytearray, object, int | None, object) -> tuple[str, bytes | bytearray] if length is None: return decode_string(data), b'' @@ -450,10 +524,12 @@ class String16(ValueField): structcode = None def __init__(self, name, pad = 1): + # type: (str, int) -> None ValueField.__init__(self, name) self.pad = pad def pack_value(self, val): + # type: (Sequence[object]) -> tuple[bytes, int, None] """Convert 8-byte string into 16-byte list""" if isinstance(val, bytes): val = list(iterbytes(val)) @@ -468,6 +544,7 @@ def pack_value(self, val): return struct.pack('>' + 'H' * slen, *val) + pad, slen, None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, int | Literal['odd', 'even'] | None, object) -> tuple[tuple[Any, ...], _SliceableBuffer] if length == 'odd': length = len(data) // 2 - 1 elif length == 'even': @@ -494,11 +571,13 @@ class List(ValueField): structcode = None def __init__(self, name, type, pad = 1): + # type: (str, Struct | ScalarObj | ResourceObj | ClassInfoClass | type[ValueField], int) -> None ValueField.__init__(self, name) self.type = type self.pad = pad def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display | None, SupportsIndex | None, object) -> tuple[list[DictWrapper | None], _SliceableBuffer] if length is None: ret = [] if self.type.structcode is None: @@ -555,6 +634,7 @@ def parse_binary_value(self, data, display, length, format): return ret, data def pack_value(self, val): + # type: (Sequence[object] | dict[str, object]) -> tuple[bytes, int, None] # Single-char values, we'll assume that means integer lists. if self.type.structcode and len(self.type.structcode) == 1: if self.type.check_value is not None: @@ -577,13 +657,16 @@ def pack_value(self, val): class FixedList(List): def __init__(self, name, size, type, pad = 1): + # type: (str, int, Struct | ScalarObj, int) -> None List.__init__(self, name, type, pad) self.size = size def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display | None, object, object) -> tuple[list[DictWrapper | None], _SliceableBuffer] return List.parse_binary_value(self, data, display, self.size, format) def pack_value(self, val): + # type: (Sequence[object] | dict[str, object]) -> tuple[bytes, int, None] if len(val) != self.size: raise BadDataError('length mismatch for FixedList %s' % self.name) return List.pack_value(self, val) @@ -591,21 +674,26 @@ def pack_value(self, val): class Object(ValueField): def __init__(self, name, type, default = None): + # type: (str, Struct, int | None) -> None ValueField.__init__(self, name, default) self.type = type self.structcode = self.type.structcode self.structvalues = self.type.structvalues def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display | None, object, object) -> tuple[DictWrapper, _SliceableBuffer] return self.type.parse_binary(data, display) def parse_value(self, val, display): + # type: (_SliceableBuffer, display.Display | None) -> DictWrapper return self.type.parse_value(val, display) def pack_value(self, val): + # type: (tuple[object, ...] | dict[str, object] | DictWrapper) -> bytes return self.type.pack_value(val) def check_value(self, val): + # type: (tuple[_T, ...] | dict[str, _T] | DictWrapper) -> list[_T] if isinstance(val, tuple): vals = [] i = 0 @@ -648,6 +736,7 @@ class PropertyData(ValueField): structcode = None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, _IntNew | None, int) -> tuple[tuple[int, _SliceableBuffer] | None, _SliceableBuffer] if length is None: length = len(data) // (format // 8) else: @@ -671,6 +760,7 @@ def parse_binary_value(self, data, display, length, format): return ret, data def pack_value(self, value): + # type: (tuple[int, Sequence[float] | Sequence[str]]) -> tuple[bytes, int, Literal[8, 16, 32]] fmt, val = value if fmt not in (8, 16, 32): @@ -704,14 +794,17 @@ def pack_value(self, value): class FixedPropertyData(PropertyData): def __init__(self, name, size): + # type: (str, int) -> None PropertyData.__init__(self, name) self.size = size def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, _IntNew | None, int) -> tuple[tuple[int, _SliceableBuffer] | None, _SliceableBuffer] return PropertyData.parse_binary_value(self, data, display, self.size // (format // 8), format) def pack_value(self, value): + # type: (tuple[int, Sequence[float] | Sequence[str]]) -> tuple[bytes, int, Literal[8, 16, 32]] data, dlen, fmt = PropertyData.pack_value(self, value) if len(data) != self.size: @@ -727,10 +820,11 @@ class ValueList(Field): default = 'usekeywords' def __init__(self, name, mask, pad, *fields): + # type: (str, int, int, _HasStructCodeAndName) -> None self.name = name self.maskcode = '={0}{1}x'.format(unsigned_codes[mask], pad).encode() self.maskcodelen = struct.calcsize(self.maskcode) - self.fields = [] + self.fields = [] # type: list[tuple[_HasStructCodeAndName, int]] flag = 1 for f in fields: @@ -739,6 +833,7 @@ def __init__(self, name, mask, pad, *fields): flag = flag << 1 def pack_value(self, arg, keys): + # type: (str | dict[str, object], dict[str, object]) -> tuple[bytes, None, None] mask = 0 data = b'' @@ -759,6 +854,7 @@ def pack_value(self, arg, keys): return struct.pack(self.maskcode, mask) + data, None, None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display | None, object, object) -> tuple[DictWrapper, _SliceableBuffer] r = {} mask = int(struct.unpack(self.maskcode, data[:self.maskcodelen])[0]) @@ -788,6 +884,7 @@ class KeyboardMapping(ValueField): structcode = None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, int | None, int) -> tuple[list[int], _SliceableBuffer] if length is None: dlen = len(data) else: @@ -802,6 +899,7 @@ def parse_binary_value(self, data, display, length, format): return ret, data[dlen:] def pack_value(self, value): + # type: (Sequence[Sequence[object]]) -> tuple[bytes, int, int] keycodes = 0 for v in value: keycodes = max(keycodes, len(v)) @@ -821,6 +919,7 @@ class ModifierMapping(ValueField): structcode = None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, object, object, int) -> tuple[list[array[int]], _SliceableBuffer] a = array(array_unsigned_codes[1], data[:8 * format]) ret = [] @@ -830,6 +929,7 @@ def parse_binary_value(self, data, display, length, format): return ret, data[8 * format:] def pack_value(self, value): + # type: (Sequence[Sequence[object]] | tuple[Sequence[object], Sequence[object], Sequence[object], Sequence[object], Sequence[object], Sequence[object], Sequence[object], Sequence[object]]) -> tuple[bytes, int, int] if len(value) != 8: raise BadDataError('ModifierMapping list should have eight elements') @@ -851,15 +951,17 @@ class EventField(ValueField): structcode = None def pack_value(self, value): + # type: (Event) -> tuple[_SliceableBuffer, None, None] if not isinstance(value, Event): raise BadDataError('%s is not an Event for field %s' % (value, self.name)) return value._binary, None, None def parse_binary_value(self, data, display, length, format): + # type: (_SliceableBuffer, display.Display, object, object) -> tuple[Event, _SliceableBuffer] from . import event - estruct = display.event_classes.get(byte2int(data) & 0x7f, event.AnyEvent) + estruct = display.event_classes.get(byte2int(data) & 0x7f, event.AnyEvent) # type: dict[int, type[Event]] | type[Event] if type(estruct) == dict: # this etype refers to a set of sub-events with individual subcodes estruct = estruct[indexbytes(data, 1)] @@ -888,12 +990,16 @@ class ResourceObj(object): structvalues = 1 def __init__(self, class_name): + # type: (str) -> None self.class_name = class_name self.check_value = None def parse_value(self, value, display): + # type: (int, _BaseDisplay | None) -> int | _ResourceBaseClass # if not display: # return value + if not display: + return value c = display.get_resource_class(self.class_name) if c: return c(display, value) @@ -907,9 +1013,11 @@ class StrClass(object): structcode = None def pack_value(self, val): + # type: (str) -> bytes return (chr(len(val)) + val).encode() def parse_binary(self, data, display): + # type: (bytes | bytearray, object) -> tuple[str, bytes | bytearray] slen = byte2int(data) + 1 return decode_string(data[1:slen]), data[slen:] @@ -939,14 +1047,15 @@ class Field. The fields of a structure are given as arguments """ def __init__(self, *fields): + # type: (Field) -> None self.fields = fields # Structures for to_binary, parse_value and parse_binary self.static_codes = '=' self.static_values = 0 - self.static_fields = [] + self.static_fields = [] # type: list[Field] self.static_size = None - self.var_fields = [] + self.var_fields = [] # type: list[Field] for f in self.fields: # Append structcode if there is one and we haven't @@ -980,6 +1089,7 @@ def __init__(self, *fields): # object def to_binary(self, *varargs, **keys): + # type: (object, object) -> bytes """data = s.to_binary(...) Convert Python values into the binary representation. The @@ -1014,7 +1124,7 @@ def to_binary(self, *varargs, **keys): formats = {} for f in self.var_fields: - if f.keyword_args: + if f.keyword_args: # f is ValueList v, l, fm = f.pack_value(field_args[f.name], keys) else: v, l, fm = f.pack_value(field_args[f.name]) @@ -1071,6 +1181,7 @@ def to_binary(self, *varargs, **keys): def pack_value(self, value): + # type: (tuple[object, ...] | dict[str, object] | DictWrapper) -> bytes """ This function allows Struct objects to be used in List and Object fields. Each item represents the arguments to pass to @@ -1088,7 +1199,23 @@ def pack_value(self, value): raise BadDataError('%s is not a tuple or a list' % (value)) + if TYPE_CHECKING: + # Structs generate their attributes + # TODO: Create a specific type-only class for all instances of `Struct` + def __getattr__(self, attr): + # type: (str) -> Any + pass + + @overload + def parse_value(self, val, display, rawdict): + # type: (_SliceableBuffer, display.Display | None, Literal[True]) -> dict[str, Any] + pass + @overload + def parse_value(self, val, display, rawdict = False): + # type: (_SliceableBuffer, display.Display | None, Literal[False]) -> DictWrapper + pass def parse_value(self, val, display, rawdict = False): + # type: (_SliceableBuffer, display.Display | None, bool) -> dict[str, Any] | DictWrapper """This function is used by List and Object fields to convert Struct objects with no var_fields into Python values. @@ -1131,7 +1258,17 @@ def parse_value(self, val, display, rawdict = False): return DictWrapper(ret) return ret + if TYPE_CHECKING: + @overload + def parse_binary(self, data, display, rawdict): + # type: (_SliceableBuffer, display.Display | None, Literal[True]) -> tuple[dict[str, Any], _SliceableBuffer] + pass + @overload + def parse_binary(self, data, display, rawdict = False): + # type: (_SliceableBuffer, display.Display | None, Literal[False]) -> tuple[DictWrapper , _SliceableBuffer] + pass def parse_binary(self, data, display, rawdict = False): + # type: (_SliceableBuffer, display.Display | None, bool) -> tuple[DictWrapper | dict[str, Any], _SliceableBuffer] """values, remdata = s.parse_binary(data, display, rawdict = False) @@ -1152,8 +1289,8 @@ def parse_binary(self, data, display, rawdict = False): """ ret = {} val = struct.unpack(self.static_codes, data[:self.static_size]) - lengths = {} - formats = {} + lengths = {} # type: dict[str, int] + formats = {} # type: dict[str, int] vno = 0 for f in self.static_fields: @@ -1215,6 +1352,7 @@ class TextElements8(ValueField): String8('string', pad = 0) ) def pack_value(self, value): + # type: (Iterable[Field | str | bytes | tuple[Sequence[object], ...] | dict[str, Sequence[object]] | DictWrapper]) -> tuple[bytes, None, None] data = b'' args = {} @@ -1256,7 +1394,8 @@ def pack_value(self, value): return data + b'\0' * ((4 - dlen % 4) % 4), None, None def parse_binary_value(self, data, display, length, format): - values = [] + # type: (_SliceableBuffer, display.Display | None, object, object) -> tuple[list[DictWrapper], Literal[b'']] + values = [] # type: list[DictWrapper] while 1: if len(data) < 2: break @@ -1287,7 +1426,10 @@ class TextElements16(TextElements8): class GetAttrData(object): + # GetAttrData classes get their attributes dynamically + # TODO: Complete all classes inheriting from GetAttrData def __getattr__(self, attr): + # type: (str) -> Any try: if self._data: return self._data[attr] @@ -1295,24 +1437,34 @@ def __getattr__(self, attr): raise AttributeError(attr) except KeyError: raise AttributeError(attr) + if TYPE_CHECKING: + def __setattr__(self, __name, __value): + # type: (str, Any) -> None + pass class DictWrapper(GetAttrData): def __init__(self, dict): + # type: (dict[str, object]) -> None self.__dict__['_data'] = dict def __getitem__(self, key): + # type: (str) -> object return self._data[key] def __setitem__(self, key, value): + # type: (str, object) -> None self._data[key] = value def __delitem__(self, key): + # type: (str) -> None del self._data[key] def __setattr__(self, key, value): + # type: (str, object) -> None self._data[key] = value def __delattr__(self, key): + # type: (str) -> None del self._data[key] def __str__(self): @@ -1322,18 +1474,21 @@ def __repr__(self): return '%s(%s)' % (self.__class__.__name__, repr(self._data)) def __lt__(self, other): + # type: (object) -> bool if isinstance(other, DictWrapper): return self._data < other._data else: return self._data < other def __gt__(self, other): + # type: (object) -> bool if isinstance(other, DictWrapper): return self._data > other._data else: return self._data > other def __eq__(self, other): + # type: (object) -> bool if isinstance(other, DictWrapper): return self._data == other._data else: @@ -1342,9 +1497,10 @@ def __eq__(self, other): class Request(object): def __init__(self, display, onerror = None, *args, **keys): + # type: (_BaseDisplay, _ErrorHandler[object] | None, object, object) -> None self._errorhandler = onerror self._binary = self._request.to_binary(*args, **keys) - self._serial = None + self._serial = None # type: int | None display.send_request(self, onerror is not None) def _set_error(self, error): @@ -1355,9 +1511,10 @@ def _set_error(self, error): class ReplyRequest(GetAttrData): def __init__(self, display, defer = False, *args, **keys): + # type: (display.Display, bool, object, object) -> None self._display = display self._binary = self._request.to_binary(*args, **keys) - self._serial = None + self._serial = None # type: int | None self._data = None self._error = None @@ -1405,6 +1562,7 @@ def __repr__(self): class Event(GetAttrData): def __init__(self, binarydata = None, display = None, **keys): + # type: (_SliceableBuffer | None, display.Display | None, object) -> None if binarydata: self._binary = binarydata self._data, data = self._fields.parse_binary(binarydata, display, @@ -1436,18 +1594,21 @@ def __repr__(self): return '%s(%s)' % (self.__class__.__name__, kws) def __lt__(self, other): + # type: (object) -> bool if isinstance(other, Event): return self._data < other._data else: return self._data < other def __gt__(self, other): + # type: (object) -> bool if isinstance(other, Event): return self._data > other._data else: return self._data > other def __eq__(self, other): + # type: (object) -> bool if isinstance(other, Event): return self._data == other._data else: @@ -1455,9 +1616,14 @@ def __eq__(self, other): def call_error_handler(handler, error, request): + # type: (_ErrorHandler[_T], XError, Request | None) -> _T | Literal[0] try: return handler(error, request) except: sys.stderr.write('Exception raised by error handler.\n') traceback.print_exc() return 0 + +if TYPE_CHECKING: + # What about StrClass, Mask, RawField, SizeOf, FP1616 and FP3232 ? + _HasStructCodeAndName = Union[Pad, Opcode, ReplyCode, LengthField, FormatField, Int8, Int16, Int32, Card8, Card16, Card32, Bool, Set, FixedBinary] diff --git a/Xlib/protocol/structs.py b/Xlib/protocol/structs.py index 1661440..430cbac 100644 --- a/Xlib/protocol/structs.py +++ b/Xlib/protocol/structs.py @@ -25,7 +25,11 @@ # Xlib.protocol modules from . import rq +# TODO: Complete all classes using WindowValues and GCValues +# Currently *object is used to represent the ValueList instead of the possible attribute types + def WindowValues(arg): + # type: (str) -> rq.ValueList return rq.ValueList( arg, 4, 0, rq.Pixmap('background_pixmap'), rq.Card32('background_pixel'), @@ -46,6 +50,7 @@ def WindowValues(arg): ) def GCValues(arg): + # type: (str) -> rq.ValueList return rq.ValueList( arg, 4, 0, rq.Set('function', 1, (X.GXclear, X.GXand, X.GXandReverse, diff --git a/Xlib/py.typed b/Xlib/py.typed new file mode 100644 index 0000000..4521c3f --- /dev/null +++ b/Xlib/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. The Xlib package uses inline types. diff --git a/Xlib/rdb.py b/Xlib/rdb.py index 03b06e2..3368bc6 100644 --- a/Xlib/rdb.py +++ b/Xlib/rdb.py @@ -31,6 +31,21 @@ # Xlib modules from .support import lock +try: + from typing import TYPE_CHECKING, Any, TypeVar, Protocol, overload +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence, Mapping + from _typeshed import SupportsRead, SupportsDunderLT, SupportsDunderGT + from Xlib import display + _T = TypeVar("_T") + _T_contra = TypeVar("_T", contravariant=True) + class SupportsComparisons(SupportsDunderLT[_T_contra], SupportsDunderGT[_T_contra], Protocol): pass + _DB = dict[str, tuple["_DB", ...]] + # This can be a bit annoying due to dict invariance, so making a parameter-specific alias + _DB_Param = dict[str, Any] + # Set up a few regexpes for parsing string representation of resources comment_re = re.compile(r'^\s*!') @@ -52,7 +67,8 @@ class OptionError(Exception): class ResourceDB(object): def __init__(self, file = None, string = None, resources = None): - self.db = {} + # type: (bytes | SupportsRead[str] | None, str | None, Iterable[tuple[str, object]] | None) -> None + self.db = {} # type: _DB self.lock = lock.allocate_lock() if file is not None: @@ -63,6 +79,7 @@ def __init__(self, file = None, string = None, resources = None): self.insert_resources(resources) def insert_file(self, file): + # type: (bytes | SupportsRead[str]) -> None """insert_file(file) Load resources entries from FILE, and insert them into the @@ -77,6 +94,7 @@ def insert_file(self, file): def insert_string(self, data): + # type: (str) -> None """insert_string(data) Insert the resources entries in the string DATA into the @@ -137,6 +155,7 @@ def insert_string(self, data): def insert_resources(self, resources): + # type: (Iterable[tuple[str, object]]) -> None """insert_resources(resources) Insert all resources entries in the list RESOURCES into the @@ -152,6 +171,7 @@ def insert_resources(self, resources): self.insert(res, value) def insert(self, resource, value): + # type: (str, object) -> None """insert(resource, value) Insert a resource entry into the database. RESOURCE is a @@ -191,6 +211,7 @@ def insert(self, resource, value): self.lock.release() def __getitem__(self, keys_tuple): + # type: (tuple[str, str]) -> Any """db[name, class] Return the value matching the resource identified by NAME and @@ -303,7 +324,17 @@ def __getitem__(self, keys_tuple): finally: self.lock.release() + if TYPE_CHECKING: + @overload + def get(self, res, cls, default = None): + # type: (str, str, None) -> Any + pass + @overload + def get(self, res, cls, default): + # type: (str, str, _T) -> _T + pass def get(self, res, cls, default = None): + # type: (str, str, object) -> Any """get(name, class [, default]) Return the value matching the resource identified by NAME and @@ -318,6 +349,7 @@ def get(self, res, cls, default = None): return default def update(self, db): + # type: (ResourceDB) -> None """update(db) Update this database with all resources entries in the resource @@ -341,6 +373,7 @@ def output(self): return text def getopt(self, name, argv, opts): + # type: (str, Sequence[str], Mapping[str, Option]) -> Sequence[str] """getopt(name, argv, opts) Parse X command line options, inserting the recognised options @@ -452,6 +485,7 @@ def value(self): # def bin_insert(list, element): + # type: (list[SupportsComparisons[_T]], SupportsComparisons[_T]) -> None """bin_insert(list, element) Insert ELEMENT into LIST. LIST must be sorted, and ELEMENT will @@ -487,6 +521,7 @@ def bin_insert(list, element): # def update_db(dest, src): + # type: (_DB_Param, _DB_Param) -> None for comp, group in src.items(): # DEST already contains this component, update it @@ -507,9 +542,11 @@ def update_db(dest, src): dest[comp] = copy_group(group) def copy_group(group): + # type: (tuple[_DB_Param, ...]) -> tuple[_DB, ...] return (copy_db(group[0]), copy_db(group[1])) + group[2:] def copy_db(db): + # type: (_DB_Param) -> _DB newdb = {} for comp, group in db.items(): newdb[comp] = copy_group(group) @@ -522,6 +559,7 @@ def copy_db(db): # def output_db(prefix, db): + # type: (str, _DB_Param) -> str res = '' for comp, group in db.items(): @@ -536,6 +574,7 @@ def output_db(prefix, db): return res def output_escape(value): + # type: (object) -> str value = str(value) if not value: return value @@ -564,39 +603,47 @@ def __init__(self): pass def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] pass class NoArg(Option): """Value is provided to constructor.""" def __init__(self, specifier, value): + # type: (str, object) -> None self.specifier = specifier self.value = value def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] db.insert(name + self.specifier, self.value) return args[1:] class IsArg(Option): """Value is the option string itself.""" def __init__(self, specifier): + # type: (str) -> None self.specifier = specifier def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] db.insert(name + self.specifier, args[0]) return args[1:] class SepArg(Option): """Value is the next argument.""" def __init__(self, specifier): + # type: (str) -> None self.specifier = specifier def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] db.insert(name + self.specifier, args[1]) return args[2:] class ResArgClass(Option): """Resource and value in the next argument.""" def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[str]) -> Sequence[str] db.insert_string(args[1]) return args[2:] @@ -605,6 +652,7 @@ def parse(self, name, db, args): class SkipArgClass(Option): """Ignore this option and next argument.""" def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] return args[2:] SkipArg = SkipArgClass() @@ -612,6 +660,7 @@ def parse(self, name, db, args): class SkipLineClass(Option): """Ignore rest of the arguments.""" def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] return [] SkipLine = SkipLineClass() @@ -619,14 +668,17 @@ def parse(self, name, db, args): class SkipNArgs(Option): """Ignore this option and the next COUNT arguments.""" def __init__(self, count): + # type: (int) -> None self.count = count def parse(self, name, db, args): + # type: (str, ResourceDB, Sequence[_T]) -> Sequence[_T] return args[1 + self.count:] def get_display_opts(options, argv = sys.argv): + # type: (Mapping[str, Option], Sequence[str]) -> tuple[display.Display, str, ResourceDB, Sequence[str]] """display, name, db, args = get_display_opts(options, [argv]) Parse X OPTIONS from ARGV (or sys.argv if not provided). diff --git a/Xlib/support/connect.py b/Xlib/support/connect.py index 4db4c2f..ba2b635 100644 --- a/Xlib/support/connect.py +++ b/Xlib/support/connect.py @@ -22,6 +22,19 @@ import sys import importlib +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + if sys.platform == 'OpenVMS': + from Xlib.support.vms_connect import get_display as get_display, get_socket as get_socket + else: + from Xlib.support.unix_connect import get_display as get_display, get_socket as get_socket + import socket + from Xlib.support.unix_connect import _Protocol + from _socket import _Address + # List the modules which contain the corresponding functions _display_mods = { @@ -56,6 +69,7 @@ def _relative_import(modname): def get_display(display): + # type: (str | None) -> tuple[str, str | None, str | None, int, int] """dname, protocol, host, dno, screen = get_display(display) Parse DISPLAY into its components. If DISPLAY is None, use @@ -74,6 +88,7 @@ def get_display(display): def get_socket(dname, protocol, host, dno): + # type: (_Address, _Protocol, _Address | None, int) -> socket.socket """socket = get_socket(dname, protocol, host, dno) Connect to the display specified by DNAME, PROTOCOL, HOST and DNO, which @@ -88,6 +103,7 @@ def get_socket(dname, protocol, host, dno): def get_auth(sock, dname, protocol, host, dno): + # type: (socket.socket, object, _Protocol, object, int) -> tuple[bytes, bytes] """auth_name, auth_data = get_auth(sock, dname, protocol, host, dno) Return authentication data for the display on the other side of diff --git a/Xlib/support/lock.py b/Xlib/support/lock.py index 6eee31f..fd1af54 100644 --- a/Xlib/support/lock.py +++ b/Xlib/support/lock.py @@ -34,6 +34,7 @@ def __init__(self): self.acquire = self.release = self.locked = self.__noop def __noop(self, *args): + # type: (object) -> None return diff --git a/Xlib/support/unix_connect.py b/Xlib/support/unix_connect.py index bd690c1..7ebd09b 100644 --- a/Xlib/support/unix_connect.py +++ b/Xlib/support/unix_connect.py @@ -19,6 +19,7 @@ # Suite 330, # Boston, MA 02111-1307 USA +import sys import re import os import platform @@ -28,9 +29,16 @@ SUPPORTED_PROTOCOLS = (None, 'tcp', 'unix') +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + _Protocol = type(SUPPORTED_PROTOCOLS) + from _socket import _Address + # Darwin funky socket. -uname = platform.uname() -if (uname[0] == 'Darwin') and ([int(x) for x in uname[2].split('.')] >= [9, 0]): +if sys.platform == 'darwin' and ([int(x) for x in platform.release().split('.')] >= [9, 0]): SUPPORTED_PROTOCOLS += ('darwin',) DARWIN_DISPLAY_RE = re.compile(r'^/private/tmp/[-:a-zA-Z0-9._]*:(?P[0-9]+)(\.(?P[0-9]+))?$') @@ -38,13 +46,14 @@ def get_display(display): + # type: (str | None) -> tuple[str, str | None, str | None, int, int] # Use $DISPLAY if display isn't provided if display is None: display = os.environ.get('DISPLAY', '') - re_list = [(DISPLAY_RE, {})] + re_list = [(DISPLAY_RE, {})] # type: list[tuple[re.Pattern[str], dict[str, str]]] - if 'darwin' in SUPPORTED_PROTOCOLS: + if sys.platform == 'darwin' and 'darwin' in SUPPORTED_PROTOCOLS: re_list.insert(0, (DARWIN_DISPLAY_RE, {'protocol': 'darwin'})) for re, defaults in re_list: @@ -84,6 +93,7 @@ def _get_unix_socket(address): def get_socket(dname, protocol, host, dno): + # type: (_Address, _Protocol, _Address | None, int) -> socket.socket assert protocol in SUPPORTED_PROTOCOLS try: # Darwin funky socket. @@ -144,6 +154,7 @@ def _ensure_not_inheritable(sock): def new_get_auth(sock, dname, protocol, host, dno): + # type: (socket.socket, object, _Protocol, object, int) -> tuple[bytes, bytes] assert protocol in SUPPORTED_PROTOCOLS # Translate socket address into the xauth domain if protocol == 'darwin': @@ -184,6 +195,7 @@ def new_get_auth(sock, dname, protocol, host, dno): def old_get_auth(sock, dname, host, dno): + # type: (object, _Address, object, object) -> tuple[str, bytes] # Find authorization cookie auth_name = auth_data = b'' diff --git a/Xlib/support/vms_connect.py b/Xlib/support/vms_connect.py index 3c53695..d1c7dc6 100644 --- a/Xlib/support/vms_connect.py +++ b/Xlib/support/vms_connect.py @@ -24,10 +24,15 @@ from Xlib import error +try: + from _socket import _Address +except ImportError: + pass + display_re = re.compile(r'^([-a-zA-Z0-9._]*):([0-9]+)(\.([0-9]+))?$') def get_display(display): - + # type: (str | None) -> tuple[str, None, str, int, int] # Use dummy display if none is set. We really should # check DECW$DISPLAY instead, but that has to wait @@ -56,6 +61,7 @@ def get_display(display): def get_socket(dname, protocol, host, dno): + # type: (_Address, object, _Address, int) -> socket.socket try: # Always use TCP/IP sockets. Later it would be nice to # be able to use DECNET och LOCAL connections. @@ -70,5 +76,6 @@ def get_socket(dname, protocol, host, dno): def get_auth(sock, dname, host, dno): + # type: (object, object, object, object) -> tuple[str, str] # VMS doesn't have xauth return '', '' diff --git a/Xlib/xauth.py b/Xlib/xauth.py index 303bd49..335d180 100644 --- a/Xlib/xauth.py +++ b/Xlib/xauth.py @@ -33,6 +33,7 @@ class Xauthority(object): def __init__(self, filename = None): + # type: (str | bytes | os.PathLike[str] | os.PathLike[bytes] | int | None) -> None if filename is None: filename = os.environ.get('XAUTHORITY') @@ -49,7 +50,7 @@ def __init__(self, filename = None): except IOError as err: raise error.XauthError('could not read from {0}: {1}'.format(filename, err)) - self.entries = [] + self.entries = [] # type: list[tuple[bytes, bytes, bytes, bytes, bytes]] # entry format (all shorts in big-endian) # short family; @@ -99,11 +100,12 @@ def __len__(self): return len(self.entries) def __getitem__(self, i): + # type: (int) -> tuple[bytes, bytes, bytes, bytes, bytes] return self.entries[i] def get_best_auth(self, family, address, dispno, types = ( b"MIT-MAGIC-COOKIE-1", )): - + # type: (bytes, bytes, bytes, tuple[bytes]) -> tuple[bytes, bytes] """Find an authentication entry matching FAMILY, ADDRESS and DISPNO. diff --git a/Xlib/xobject/colormap.py b/Xlib/xobject/colormap.py index 033fb49..57ed8d1 100644 --- a/Xlib/xobject/colormap.py +++ b/Xlib/xobject/colormap.py @@ -26,6 +26,16 @@ import re +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable, Sequence + from Xlib.protocol import rq + _T = TypeVar("_T") + _ErrorHandler = Callable[[error.XError, Optional[rq.Request]], _T] + rgb_res = [ re.compile(r'\Argb:([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})\Z'), re.compile(r'\A#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])\Z'), @@ -38,6 +48,7 @@ class Colormap(resource.Resource): __colormap__ = resource.Resource.__resource__ def free(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.FreeColormap(display = self.display, onerror = onerror, cmap = self.id) @@ -45,6 +56,7 @@ def free(self, onerror = None): self.display.free_resource_id(self.id) def copy_colormap_and_free(self, scr_cmap): + # type: (int) -> Colormap mid = self.display.allocate_resource_id() request.CopyColormapAndFree(display = self.display, mid = mid, @@ -54,11 +66,13 @@ def copy_colormap_and_free(self, scr_cmap): return cls(self.display, mid, owner = 1) def install_colormap(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.InstallColormap(display = self.display, onerror = onerror, cmap = self.id) def uninstall_colormap(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.UninstallColormap(display = self.display, onerror = onerror, cmap = self.id) @@ -71,6 +85,7 @@ def alloc_color(self, red, green, blue): blue = blue) def alloc_named_color(self, name): + # type: (str) -> request.AllocColor | request.AllocNamedColor | None for r in rgb_res: m = r.match(name) if m: @@ -93,6 +108,7 @@ def alloc_named_color(self, name): return None def alloc_color_cells(self, contiguous, colors, planes): + # type: (bool, int, int) -> request.AllocColorCells return request.AllocColorCells(display = self.display, contiguous = contiguous, cmap = self.id, @@ -100,6 +116,7 @@ def alloc_color_cells(self, contiguous, colors, planes): planes = planes) def alloc_color_planes(self, contiguous, colors, red, green, blue): + # type: (bool, int, int, int, int) -> request.AllocColorPlanes return request.AllocColorPlanes(display = self.display, contiguous = contiguous, cmap = self.id, @@ -109,6 +126,7 @@ def alloc_color_planes(self, contiguous, colors, red, green, blue): blue = blue) def free_colors(self, pixels, plane_mask, onerror = None): + # type: (Sequence[int], int, _ErrorHandler[object] | None) -> None request.FreeColors(display = self.display, onerror = onerror, cmap = self.id, @@ -116,12 +134,14 @@ def free_colors(self, pixels, plane_mask, onerror = None): pixels = pixels) def store_colors(self, items, onerror = None): + # type: (dict[str, int], _ErrorHandler[object] | None) -> None request.StoreColors(display = self.display, onerror = onerror, cmap = self.id, items = items) def store_named_color(self, name, pixel, flags, onerror = None): + # type: (str, int, int, _ErrorHandler[object] | None) -> None request.StoreNamedColor(display = self.display, onerror = onerror, flags = flags, @@ -130,12 +150,14 @@ def store_named_color(self, name, pixel, flags, onerror = None): name = name) def query_colors(self, pixels): + # type: (Sequence[int]) -> rq.Struct r = request.QueryColors(display = self.display, cmap = self.id, pixels = pixels) return r.colors def lookup_color(self, name): + # type: (str) -> request.LookupColor return request.LookupColor(display = self.display, cmap = self.id, name = name) diff --git a/Xlib/xobject/cursor.py b/Xlib/xobject/cursor.py index 432e4fd..a9932aa 100644 --- a/Xlib/xobject/cursor.py +++ b/Xlib/xobject/cursor.py @@ -23,16 +23,29 @@ from . import resource +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from Xlib.error import XError + from Xlib.protocol.rq import Request + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[Request]], _T] + class Cursor(resource.Resource): __cursor__ = resource.Resource.__resource__ def free(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.FreeCursor(display = self.display, onerror = onerror, cursor = self.id) self.display.free_resource_id(self.id) def recolor(self, foreground, background, onerror=None): + # type: (tuple[int, int, int], tuple[int, int, int], _ErrorHandler[object] | None) -> None fore_red, fore_green, fore_blue = foreground back_red, back_green, back_blue = background diff --git a/Xlib/xobject/drawable.py b/Xlib/xobject/drawable.py index f8e625f..8492a3e 100644 --- a/Xlib/xobject/drawable.py +++ b/Xlib/xobject/drawable.py @@ -31,6 +31,17 @@ # Inter-client communication conventions from . import icccm +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable, Sequence, Iterable + from Xlib.error import XError + from PIL import Image + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[rq.Request]], _T] + class Drawable(resource.Resource): __drawable__ = resource.Resource.__resource__ @@ -39,6 +50,7 @@ def get_geometry(self): drawable = self) def create_pixmap(self, width, height, depth): + # type: (int, int, int) -> Pixmap pid = self.display.allocate_resource_id() request.CreatePixmap(display = self.display, depth = depth, @@ -51,6 +63,7 @@ def create_pixmap(self, width, height, depth): return cls(self.display, pid, owner = 1) def create_gc(self, **keys): + # type: (object) -> fontable.GC cid = self.display.allocate_resource_id() request.CreateGC(display = self.display, cid = cid, @@ -61,6 +74,7 @@ def create_gc(self, **keys): return cls(self.display, cid, owner = 1) def copy_area(self, gc, src_drawable, src_x, src_y, width, height, dst_x, dst_y, onerror = None): + # type: (int, int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.CopyArea(display = self.display, onerror = onerror, src_drawable = src_drawable, @@ -75,6 +89,7 @@ def copy_area(self, gc, src_drawable, src_x, src_y, width, height, dst_x, dst_y, def copy_plane(self, gc, src_drawable, src_x, src_y, width, height, dst_x, dst_y, bit_plane, onerror = None): + # type: (int, int, int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.CopyPlane(display = self.display, onerror = onerror, src_drawable = src_drawable, @@ -89,6 +104,7 @@ def copy_plane(self, gc, src_drawable, src_x, src_y, width, height, bit_plane = bit_plane) def poly_point(self, gc, coord_mode, points, onerror = None): + # type: (int, int, Sequence[tuple[int, int]], _ErrorHandler[object] | None) -> None request.PolyPoint(display = self.display, onerror = onerror, coord_mode = coord_mode, @@ -97,6 +113,7 @@ def poly_point(self, gc, coord_mode, points, onerror = None): points = points) def point(self, gc, x, y, onerror = None): + # type: (int, int, int, _ErrorHandler[object] | None) -> None request.PolyPoint(display = self.display, onerror = onerror, coord_mode = X.CoordModeOrigin, @@ -105,6 +122,7 @@ def point(self, gc, x, y, onerror = None): points = [(x, y)]) def poly_line(self, gc, coord_mode, points, onerror = None): + # type: (int, int, Sequence[tuple[int, int]], _ErrorHandler[object] | None) -> None request.PolyLine(display = self.display, onerror = onerror, coord_mode = coord_mode, @@ -113,6 +131,7 @@ def poly_line(self, gc, coord_mode, points, onerror = None): points = points) def line(self, gc, x1, y1, x2, y2, onerror = None): + # type: (int, int, int, int, int, _ErrorHandler[object] | None) -> None request.PolySegment(display = self.display, onerror = onerror, drawable = self.id, @@ -120,6 +139,7 @@ def line(self, gc, x1, y1, x2, y2, onerror = None): segments = [(x1, y1, x2, y2)]) def poly_segment(self, gc, segments, onerror = None): + # type: (int, Sequence[tuple[int, int, int, int]], _ErrorHandler[object] | None) -> None request.PolySegment(display = self.display, onerror = onerror, drawable = self.id, @@ -127,6 +147,7 @@ def poly_segment(self, gc, segments, onerror = None): segments = segments) def poly_rectangle(self, gc, rectangles, onerror = None): + # type: (int, Sequence[tuple[int, int, int, int]], _ErrorHandler[object] | None) -> None request.PolyRectangle(display = self.display, onerror = onerror, drawable = self.id, @@ -134,6 +155,7 @@ def poly_rectangle(self, gc, rectangles, onerror = None): rectangles = rectangles) def rectangle(self, gc, x, y, width, height, onerror = None): + # type: (int, int, int, int, int, _ErrorHandler[object] | None) -> None request.PolyRectangle(display = self.display, onerror = onerror, drawable = self.id, @@ -142,6 +164,7 @@ def rectangle(self, gc, x, y, width, height, onerror = None): def poly_arc(self, gc, arcs, onerror = None): + # type: (int, Sequence[tuple[int, int, int, int, int, int]], _ErrorHandler[object] | None) -> None request.PolyArc(display = self.display, onerror = onerror, drawable = self.id, @@ -149,6 +172,7 @@ def poly_arc(self, gc, arcs, onerror = None): arcs = arcs) def arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): + # type: (int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.PolyArc(display = self.display, onerror = onerror, drawable = self.id, @@ -156,6 +180,7 @@ def arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): arcs = [(x, y, width, height, angle1, angle2)]) def fill_poly(self, gc, shape, coord_mode, points, onerror = None): + # type: (int, int, int, Sequence[tuple[int, int]], _ErrorHandler[object] | None) -> None request.FillPoly(display = self.display, onerror = onerror, shape = shape, @@ -165,6 +190,7 @@ def fill_poly(self, gc, shape, coord_mode, points, onerror = None): points = points) def poly_fill_rectangle(self, gc, rectangles, onerror = None): + # type: (int, Sequence[tuple[int, int, int, int]], _ErrorHandler[object] | None) -> None request.PolyFillRectangle(display = self.display, onerror = onerror, drawable = self.id, @@ -172,6 +198,7 @@ def poly_fill_rectangle(self, gc, rectangles, onerror = None): rectangles = rectangles) def fill_rectangle(self, gc, x, y, width, height, onerror = None): + # type: (int, int, int, int, int, _ErrorHandler[object] | None) -> None request.PolyFillRectangle(display = self.display, onerror = onerror, drawable = self.id, @@ -179,6 +206,7 @@ def fill_rectangle(self, gc, x, y, width, height, onerror = None): rectangles = [(x, y, width, height)]) def poly_fill_arc(self, gc, arcs, onerror = None): + # type: (int, Sequence[tuple[int, int, int, int, int, int]], _ErrorHandler[object] | None) -> None request.PolyFillArc(display = self.display, onerror = onerror, drawable = self.id, @@ -186,6 +214,7 @@ def poly_fill_arc(self, gc, arcs, onerror = None): arcs = arcs) def fill_arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): + # type: (int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.PolyFillArc(display = self.display, onerror = onerror, drawable = self.id, @@ -195,6 +224,7 @@ def fill_arc(self, gc, x, y, width, height, angle1, angle2, onerror = None): def put_image(self, gc, x, y, width, height, format, depth, left_pad, data, onerror = None): + # type: (int, int, int, int, int, int, int, int, bytes | bytearray, _ErrorHandler[object] | None) -> None request.PutImage(display = self.display, onerror = onerror, format = format, @@ -211,6 +241,7 @@ def put_image(self, gc, x, y, width, height, format, # Trivial little method for putting PIL images. Will break on anything # but depth 1 or 24... def put_pil_image(self, gc, x, y, image, onerror = None): + # type: (int, int, int, Image.Image, _ErrorHandler[object] | None) -> None width, height = image.size if image.mode == '1': format = X.XYBitmap @@ -256,6 +287,7 @@ def put_pil_image(self, gc, x, y, image, onerror = None): def get_image(self, x, y, width, height, format, plane_mask): + # type: (int, int, int, int, int, int) -> request.GetImage return request.GetImage(display = self.display, format = format, drawable = self.id, @@ -266,6 +298,7 @@ def get_image(self, x, y, width, height, format, plane_mask): plane_mask = plane_mask) def draw_text(self, gc, x, y, text, onerror = None): + # type: (int, int, int, dict[str, str | int], _ErrorHandler[object] | None) -> None request.PolyText8(display = self.display, onerror = onerror, drawable = self.id, @@ -275,6 +308,7 @@ def draw_text(self, gc, x, y, text, onerror = None): items = [text]) def poly_text(self, gc, x, y, items, onerror = None): + # type: (int, int, int, Sequence[dict[str, str | int]], _ErrorHandler[object] | None) -> None request.PolyText8(display = self.display, onerror = onerror, drawable = self.id, @@ -284,6 +318,7 @@ def poly_text(self, gc, x, y, items, onerror = None): items = items) def poly_text_16(self, gc, x, y, items, onerror = None): + # type: (int, int, int, Sequence[dict[str, str | int]], _ErrorHandler[object] | None) -> None request.PolyText16(display = self.display, onerror = onerror, drawable = self.id, @@ -293,6 +328,7 @@ def poly_text_16(self, gc, x, y, items, onerror = None): items = items) def image_text(self, gc, x, y, string, onerror = None): + # type: (int, int, int, str, _ErrorHandler[object] | None) -> None request.ImageText8(display = self.display, onerror = onerror, drawable = self.id, @@ -302,6 +338,7 @@ def image_text(self, gc, x, y, string, onerror = None): string = string) def image_text_16(self, gc, x, y, string, onerror = None): + # type: (int, int, int, str, _ErrorHandler[object] | None) -> None request.ImageText16(display = self.display, onerror = onerror, drawable = self.id, @@ -311,6 +348,7 @@ def image_text_16(self, gc, x, y, string, onerror = None): string = string) def query_best_size(self, item_class, width, height): + # type: (int, int, int) -> request.QueryBestSize return request.QueryBestSize(display = self.display, item_class = item_class, drawable = self.id, @@ -328,6 +366,7 @@ def create_window(self, x, y, width, height, border_width, depth, visual = X.CopyFromParent, onerror = None, **keys): + # type: (int, int, int, int, int, int, int, int, _ErrorHandler[object] | None, object) -> Window wid = self.display.allocate_resource_id() request.CreateWindow(display = self.display, @@ -348,6 +387,7 @@ def create_window(self, x, y, width, height, border_width, depth, return cls(self.display, wid, owner = 1) def change_attributes(self, onerror = None, **keys): + # type: (_ErrorHandler[object] | None, object) -> None request.ChangeWindowAttributes(display = self.display, onerror = onerror, window = self.id, @@ -358,6 +398,7 @@ def get_attributes(self): window = self.id) def destroy(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.DestroyWindow(display = self.display, onerror = onerror, window = self.id) @@ -365,18 +406,21 @@ def destroy(self, onerror = None): self.display.free_resource_id(self.id) def destroy_sub_windows(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.DestroySubWindows(display = self.display, onerror = onerror, window = self.id) def change_save_set(self, mode, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None request.ChangeSaveSet(display = self.display, onerror = onerror, mode = mode, window = self.id) def reparent(self, parent, x, y, onerror = None): + # type: (int, int, int, _ErrorHandler[object] | None) -> None request.ReparentWindow(display = self.display, onerror = onerror, window = self.id, @@ -385,38 +429,45 @@ def reparent(self, parent, x, y, onerror = None): y = y) def map(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.MapWindow(display = self.display, onerror = onerror, window = self.id) def map_sub_windows(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.MapSubwindows(display = self.display, onerror = onerror, window = self.id) def unmap(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.UnmapWindow(display = self.display, onerror = onerror, window = self.id) def unmap_sub_windows(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.UnmapSubwindows(display = self.display, onerror = onerror, window = self.id) def configure(self, onerror = None, **keys): + # type: (_ErrorHandler[object] | None, object) -> None request.ConfigureWindow(display = self.display, onerror = onerror, window = self.id, attrs = keys) def circulate(self, direction, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None request.CirculateWindow(display = self.display, onerror = onerror, direction = direction, window = self.id) def raise_window(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None """alias for raising the window to the top - as in XRaiseWindow""" self.configure(onerror, stack_mode = X.Above) @@ -426,6 +477,7 @@ def query_tree(self): def change_property(self, property, property_type, format, data, mode = X.PropModeReplace, onerror = None): + # type: (int, int, int, Sequence[float] | Sequence[str], int, _ErrorHandler[object] | None) -> None request.ChangeProperty(display = self.display, onerror = onerror, @@ -437,6 +489,7 @@ def change_property(self, property, property_type, format, data, def change_text_property(self, property, property_type, data, mode = X.PropModeReplace, onerror = None): + # type: (int, int, bytes | str, int, _ErrorHandler[object] | None) -> None if not isinstance(data, bytes): if property_type == Xatom.STRING: data = data.encode(self._STRING_ENCODING) @@ -446,12 +499,14 @@ def change_text_property(self, property, property_type, data, mode=mode, onerror=onerror) def delete_property(self, property, onerror = None): + # type: (int, _ErrorHandler[object] | None) -> None request.DeleteProperty(display = self.display, onerror = onerror, window = self.id, property = property) def get_property(self, property, property_type, offset, length, delete = False): + # type: (int, int, int, int, bool) -> request.GetProperty | None r = request.GetProperty(display = self.display, delete = delete, window = self.id, @@ -469,6 +524,7 @@ def get_property(self, property, property_type, offset, length, delete = False): return None def get_full_property(self, property, property_type, sizehint = 10): + # type: (int, int, int) -> request.GetProperty | None prop = self.get_property(property, property_type, 0, sizehint) if prop: val = prop.value @@ -483,6 +539,7 @@ def get_full_property(self, property, property_type, sizehint = 10): return None def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10): + # type: (int, int, int) -> str | None prop = self.get_full_property(property, property_type, sizehint=sizehint) if prop is None or prop.format != 8: @@ -496,11 +553,13 @@ def get_full_text_property(self, property, property_type=X.AnyPropertyType, size return prop.value def list_properties(self): + # type: () -> list[int] r = request.ListProperties(display = self.display, window = self.id) return r.atoms def set_selection_owner(self, selection, time, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None request.SetSelectionOwner(display = self.display, onerror = onerror, window = self.id, @@ -508,6 +567,7 @@ def set_selection_owner(self, selection, time, onerror = None): time = time) def convert_selection(self, selection, target, property, time, onerror = None): + # type: (int, int, int, int, _ErrorHandler[object] | None) -> None request.ConvertSelection(display = self.display, onerror = onerror, requestor = self.id, @@ -517,6 +577,7 @@ def convert_selection(self, selection, target, property, time, onerror = None): time = time) def send_event(self, event, event_mask = 0, propagate = False, onerror = None): + # type: (rq.Event, int, bool, _ErrorHandler[object] | None) -> None request.SendEvent(display = self.display, onerror = onerror, propagate = propagate, @@ -527,6 +588,7 @@ def send_event(self, event, event_mask = 0, propagate = False, onerror = None): def grab_pointer(self, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time): + # type: (bool, int, int, int, int, int, int) -> None r = request.GrabPointer(display = self.display, owner_events = owner_events, @@ -542,6 +604,7 @@ def grab_pointer(self, owner_events, event_mask, def grab_button(self, button, modifiers, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, onerror = None): + # type: (int, int, bool, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.GrabButton(display = self.display, onerror = onerror, @@ -556,6 +619,7 @@ def grab_button(self, button, modifiers, owner_events, event_mask, modifiers = modifiers) def ungrab_button(self, button, modifiers, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None request.UngrabButton(display = self.display, onerror = onerror, button = button, @@ -564,6 +628,7 @@ def ungrab_button(self, button, modifiers, onerror = None): def grab_keyboard(self, owner_events, pointer_mode, keyboard_mode, time): + # type: (bool, int, int, int) -> int r = request.GrabKeyboard(display = self.display, owner_events = owner_events, grab_window = self.id, @@ -574,6 +639,7 @@ def grab_keyboard(self, owner_events, pointer_mode, keyboard_mode, time): return r.status def grab_key(self, key, modifiers, owner_events, pointer_mode, keyboard_mode, onerror = None): + # type: (int, int, bool, int, int, _ErrorHandler[object] | None) -> None request.GrabKey(display = self.display, onerror = onerror, owner_events = owner_events, @@ -584,6 +650,7 @@ def grab_key(self, key, modifiers, owner_events, pointer_mode, keyboard_mode, on keyboard_mode = keyboard_mode) def ungrab_key(self, key, modifiers, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None request.UngrabKey(display = self.display, onerror = onerror, key = key, @@ -595,6 +662,7 @@ def query_pointer(self): window = self.id) def get_motion_events(self, start, stop): + # type: (int, int) -> rq.Struct r = request.GetMotionEvents(display = self.display, window = self.id, start = start, @@ -602,6 +670,7 @@ def get_motion_events(self, start, stop): return r.events def translate_coords(self, src_window, src_x, src_y): + # type: (int, int, int) -> request.TranslateCoords return request.TranslateCoords(display = self.display, src_wid = src_window, dst_wid = self.id, @@ -610,6 +679,7 @@ def translate_coords(self, src_window, src_x, src_y): def warp_pointer(self, x, y, src_window = 0, src_x = 0, src_y = 0, src_width = 0, src_height = 0, onerror = None): + # type: (int, int, int, int, int, int, int, _ErrorHandler[object] | None) -> None request.WarpPointer(display = self.display, onerror = onerror, @@ -623,6 +693,7 @@ def warp_pointer(self, x, y, src_window = 0, src_x = 0, src_y = 0, dst_y = y) def set_input_focus(self, revert_to, time, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None request.SetInputFocus(display = self.display, onerror = onerror, revert_to = revert_to, @@ -630,6 +701,7 @@ def set_input_focus(self, revert_to, time, onerror = None): time = time) def clear_area(self, x = 0, y = 0, width = 0, height = 0, exposures = False, onerror = None): + # type: (int, int, int, int, bool, _ErrorHandler[object] | None) -> None request.ClearArea(display = self.display, onerror = onerror, exposures = exposures, @@ -640,6 +712,7 @@ def clear_area(self, x = 0, y = 0, width = 0, height = 0, exposures = False, one height = height) def create_colormap(self, visual, alloc): + # type: (int, int) -> colormap.Colormap mid = self.display.allocate_resource_id() request.CreateColormap(display = self.display, alloc = alloc, @@ -650,11 +723,13 @@ def create_colormap(self, visual, alloc): return cls(self.display, mid, owner = 1) def list_installed_colormaps(self): + # type: () -> list[colormap.Colormap] r = request.ListInstalledColormaps(display = self.display, window = self.id) return r.cmaps def rotate_properties(self, properties, delta, onerror = None): + # type: (Sequence[int], int, _ErrorHandler[object] | None) -> None request.RotateProperties(display = self.display, onerror = onerror, window = self.id, @@ -662,6 +737,7 @@ def rotate_properties(self, properties, delta, onerror = None): properties = properties) def set_wm_name(self, name, onerror = None): + # type: (bytes | str, _ErrorHandler[object] | None) -> None self.change_text_property(Xatom.WM_NAME, Xatom.STRING, name, onerror = onerror) @@ -669,6 +745,7 @@ def get_wm_name(self): return self.get_full_text_property(Xatom.WM_NAME, Xatom.STRING) def set_wm_icon_name(self, name, onerror = None): + # type: (bytes | str, _ErrorHandler[object] | None) -> None self.change_text_property(Xatom.WM_ICON_NAME, Xatom.STRING, name, onerror = onerror) @@ -676,6 +753,7 @@ def get_wm_icon_name(self): return self.get_full_text_property(Xatom.WM_ICON_NAME, Xatom.STRING) def set_wm_class(self, inst, cls, onerror = None): + # type: (str, str, _ErrorHandler[object] | None) -> None self.change_text_property(Xatom.WM_CLASS, Xatom.STRING, '%s\0%s\0' % (inst, cls), onerror = onerror) @@ -691,6 +769,7 @@ def get_wm_class(self): return parts[0], parts[1] def set_wm_transient_for(self, window, onerror = None): + # type: (Window, _ErrorHandler[object] | None) -> None self.change_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, 32, [window.id], onerror = onerror) @@ -705,11 +784,13 @@ def get_wm_transient_for(self): def set_wm_protocols(self, protocols, onerror = None): + # type: (Iterable[int], _ErrorHandler[object] | None) -> None self.change_property(self.display.get_atom('WM_PROTOCOLS'), Xatom.ATOM, 32, protocols, onerror = onerror) def get_wm_protocols(self): + # type: () -> list[int] d = self.get_full_property(self.display.get_atom('WM_PROTOCOLS'), Xatom.ATOM) if d is None or d.format != 32: return [] @@ -717,12 +798,14 @@ def get_wm_protocols(self): return d.value def set_wm_colormap_windows(self, windows, onerror = None): + # type: (Iterable[Window], _ErrorHandler[object] | None) -> None self.change_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), Xatom.WINDOW, 32, map(lambda w: w.id, windows), onerror = onerror) def get_wm_colormap_windows(self): + # type: () -> list[Window] | map[Window] d = self.get_full_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), Xatom.WINDOW) if d is None or d.format != 32: @@ -734,6 +817,7 @@ def get_wm_colormap_windows(self): def set_wm_client_machine(self, name, onerror = None): + # type: (bytes | str, _ErrorHandler[object] | None) -> None self.change_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, name, onerror = onerror) @@ -741,6 +825,7 @@ def get_wm_client_machine(self): return self.get_full_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) def set_wm_normal_hints(self, hints = {}, onerror = None, **keys): + # type: (rq.DictWrapper | dict[str, object], _ErrorHandler[object] | None, object) -> None self._set_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS, icccm.WMNormalHints, hints, keys, onerror) @@ -749,6 +834,7 @@ def get_wm_normal_hints(self): icccm.WMNormalHints) def set_wm_hints(self, hints = {}, onerror = None, **keys): + # type: (rq.DictWrapper | dict[str, object], _ErrorHandler[object] | None, object) -> None self._set_struct_prop(Xatom.WM_HINTS, Xatom.WM_HINTS, icccm.WMHints, hints, keys, onerror) @@ -757,6 +843,7 @@ def get_wm_hints(self): icccm.WMHints) def set_wm_state(self, hints = {}, onerror = None, **keys): + # type: (rq.DictWrapper | dict[str, object], _ErrorHandler[object] | None, object) -> None atom = self.display.get_atom('WM_STATE') self._set_struct_prop(atom, atom, icccm.WMState, hints, keys, onerror) @@ -765,6 +852,7 @@ def get_wm_state(self): return self._get_struct_prop(atom, atom, icccm.WMState) def set_wm_icon_size(self, hints = {}, onerror = None, **keys): + # type: (rq.DictWrapper | dict[str, object], _ErrorHandler[object] | None, object) -> None self._set_struct_prop(Xatom.WM_ICON_SIZE, Xatom.WM_ICON_SIZE, icccm.WMIconSize, hints, keys, onerror) @@ -777,6 +865,7 @@ def get_wm_icon_size(self): # Returns a DictWrapper, or None def _get_struct_prop(self, pname, ptype, pstruct): + # type: (int, int, rq.Struct) -> rq.DictWrapper | None r = self.get_property(pname, ptype, 0, pstruct.static_size // 4) if r and r.format == 32: value = rq.encode_array(r.value) @@ -791,6 +880,7 @@ def _get_struct_prop(self, pname, ptype, pstruct): # will be modified. onerror is the error handler. def _set_struct_prop(self, pname, ptype, pstruct, hints, keys, onerror): + # type: (int, int, rq.Struct, rq.DictWrapper | dict[str, object], dict[str, object], _ErrorHandler[object] | None) -> None if isinstance(hints, rq.DictWrapper): keys.update(hints._data) else: @@ -805,6 +895,7 @@ class Pixmap(Drawable): __pixmap__ = resource.Resource.__resource__ def free(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.FreePixmap(display = self.display, onerror = onerror, pixmap = self.id) @@ -812,6 +903,7 @@ def free(self, onerror = None): self.display.free_resource_id(self.id) def create_cursor(self, mask, foreground, background, x, y): + # type: (int, tuple[int, int, int], tuple[int, int, int], int, int) -> cursor.Cursor fore_red, fore_green, fore_blue = foreground back_red, back_green, back_blue = background cid = self.display.allocate_resource_id() @@ -832,4 +924,5 @@ def create_cursor(self, mask, foreground, background, x, y): def roundup(value, unit): + # type: (int, int) -> int return (value + (unit - 1)) & ~(unit - 1) diff --git a/Xlib/xobject/fontable.py b/Xlib/xobject/fontable.py index 892f266..994a501 100644 --- a/Xlib/xobject/fontable.py +++ b/Xlib/xobject/fontable.py @@ -24,6 +24,18 @@ from . import resource from . import cursor +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from Xlib.error import XError + from Xlib.protocol.rq import Request + from collections.abc import Sequence + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[Request]], _T] + class Fontable(resource.Resource): __fontable__ = resource.Resource.__resource__ @@ -32,6 +44,7 @@ def query(self): font = self.id) def query_text_extents(self, string): + # type: (str) -> request.QueryTextExtents return request.QueryTextExtents(display = self.display, font = self.id, string = string) @@ -41,6 +54,7 @@ class GC(Fontable): __gc__ = resource.Resource.__resource__ def change(self, onerror = None, **keys): + # type: (_ErrorHandler[object] | None, object) -> None request.ChangeGC(display = self.display, onerror = onerror, gc = self.id, @@ -48,6 +62,7 @@ def change(self, onerror = None, **keys): def copy(self, src_gc, mask, onerror = None): + # type: (int, int, _ErrorHandler[object] | None) -> None request.CopyGC(display = self.display, onerror = onerror, src_gc = src_gc, @@ -55,6 +70,7 @@ def copy(self, src_gc, mask, onerror = None): mask = mask) def set_dashes(self, offset, dashes, onerror = None): + # type: (int, Sequence[int], _ErrorHandler[object] | None) -> None request.SetDashes(display = self.display, onerror = onerror, gc = self.id, @@ -62,6 +78,7 @@ def set_dashes(self, offset, dashes, onerror = None): dashes = dashes) def set_clip_rectangles(self, x_origin, y_origin, rectangles, ordering, onerror = None): + # type: (int, int, Sequence[Sequence[int] | tuple[int, int, int, int]], Sequence[int] | tuple[int, int, int, int], _ErrorHandler[object] | None) -> None request.SetClipRectangles(display = self.display, onerror = onerror, ordering = ordering, @@ -70,6 +87,7 @@ def set_clip_rectangles(self, x_origin, y_origin, rectangles, ordering, onerror y_origin = y_origin, rectangles = rectangles) def free(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.FreeGC(display = self.display, onerror = onerror, gc = self.id) @@ -82,6 +100,7 @@ class Font(Fontable): __font__ = resource.Resource.__resource__ def close(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.CloseFont(display = self.display, onerror = onerror, font = self.id) @@ -89,6 +108,7 @@ def close(self, onerror = None): def create_glyph_cursor(self, mask, source_char, mask_char, foreground, background): + # type: (Font, int, int, tuple[int, int, int], tuple[int, int, int]) -> cursor.Cursor fore_red, fore_green, fore_blue = foreground back_red, back_green, back_blue = background diff --git a/Xlib/xobject/resource.py b/Xlib/xobject/resource.py index ea256ca..60b39cd 100644 --- a/Xlib/xobject/resource.py +++ b/Xlib/xobject/resource.py @@ -21,8 +21,21 @@ from Xlib.protocol import request +try: + from typing import TYPE_CHECKING, TypeVar, Optional +except ImportError: + TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Callable + from Xlib.error import XError + from Xlib.protocol.rq import Request + from Xlib.display import _BaseDisplay + _T = TypeVar("_T") + _ErrorHandler = Callable[[XError, Optional[Request]], _T] + class Resource(object): def __init__(self, display, rid, owner = 0): + # type: (_BaseDisplay, int, int) -> None self.display = display self.id = rid self.owner = owner @@ -31,6 +44,7 @@ def __resource__(self): return self.id def __eq__(self, obj): + # type: (object) -> bool if isinstance(obj, Resource): if self.display == obj.display: return self.id == obj.id @@ -40,6 +54,7 @@ def __eq__(self, obj): return id(self) == id(obj) def __ne__(self, obj): + # type: (object) -> bool return not self == obj def __hash__(self): @@ -49,6 +64,7 @@ def __repr__(self): return '<%s 0x%08x>' % (self.__class__.__name__, self.id) def kill_client(self, onerror = None): + # type: (_ErrorHandler[object] | None) -> None request.KillClient(display = self.display, onerror = onerror, resource = self.id) diff --git a/setup.py b/setup.py index 71bdb08..e11ae54 100644 --- a/setup.py +++ b/setup.py @@ -20,4 +20,5 @@ 'Xlib.support', 'Xlib.xobject' ], + package_data={"Xlib": ["Xlib/py.typed"]}, ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy