From 30d718d21adc51c71ed80533efe13c7ca532f4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 00:59:36 -0500 Subject: [PATCH 1/9] Enhance CI workflows for library handling - Added a step in `pr-validation.yml` to upload built native libraries as artifacts, reducing rebuild time in subsequent jobs. - Updated `python-publish.yml` to download these artifacts, allowing for linking without the need for a full build environment, streamlining the CI process. --- .github/workflows/pr-validation.yml | 9 ++++++++ .github/workflows/python-publish.yml | 31 ++++++++++++++-------------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 9d6cfcb..1a64630 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -81,6 +81,15 @@ jobs: make native fi + # Relays the built static libs to python-publish.yml's wheel jobs (same workflow run) so they + # don't need to rebuild ~15 native libs for yum/manylinux; those jobs only link, never build here. + - name: Upload native libraries + uses: actions/upload-artifact@v7 + with: + name: orcsome3-built-libraries + path: orcsome3_built_libraries + retention-days: 1 + - name: Validate backend stub id: stubs run: make stubs-check diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 0d5d5f9..a20d067 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -14,6 +14,9 @@ permissions: env: CIBW_SKIP: "*-musllinux_* *t-*" CIBW_BEFORE_BUILD: "pip install 'Cython>=3.0'" + # The manylinux container has none of the X11 dev headers orcsome3_backend.pyx needs; the static + # libs from `validate`'s native build are relayed in separately (see wheels jobs' download step). + CIBW_BEFORE_ALL_LINUX: "yum install -y libX11-devel libXext-devel libXScrnSaver-devel libXtst-devel" CIBW_ENVIRONMENT: "ORCSOME3_BUILD_JOBS=2" CIBW_TEST_SKIP: "*" @@ -32,14 +35,12 @@ jobs: with: persist-credentials: false - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - git cmake autoconf automake libtool pkg-config meson ninja-build \ - libx11-dev libxss-dev libxext-dev libxtst-dev curl - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # Static libs already built by `validate` (native, Linux glibc) — only linking happens here, + # so no cmake/meson/rust toolchain is needed in this job. + - uses: actions/download-artifact@v8 + with: + name: orcsome3-built-libraries + path: orcsome3_built_libraries - uses: pypa/cibuildwheel@v4.2.0 env: @@ -58,14 +59,12 @@ jobs: with: persist-credentials: false - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - git cmake autoconf automake libtool pkg-config meson ninja-build \ - libx11-dev libxss-dev libxext-dev libxtst-dev curl - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + # Static libs already built by `validate` (native, Linux glibc) — only linking happens here, + # so no cmake/meson/rust toolchain is needed in this job. + - uses: actions/download-artifact@v8 + with: + name: orcsome3-built-libraries + path: orcsome3_built_libraries - uses: pypa/cibuildwheel@v3.4.1 env: From 2fb54fa670c0d4e1056e5631ef8a414b024d8cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 01:12:04 -0500 Subject: [PATCH 2/9] Refactor pixman version handling in recipes.py - Updated the version extraction logic for the pixman library to ensure compatibility with Python 3.8 by replacing the use of str.removeprefix() with a conditional check. - Added a future import for annotations to enhance type hinting support. --- orcsome3/libs/build/recipes.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/orcsome3/libs/build/recipes.py b/orcsome3/libs/build/recipes.py index 532ac82..221a897 100644 --- a/orcsome3/libs/build/recipes.py +++ b/orcsome3/libs/build/recipes.py @@ -1,5 +1,7 @@ """Library source versions and declarative build recipes (used by engine.py).""" +from __future__ import annotations + from pathlib import Path from typing import Any @@ -216,7 +218,13 @@ def _recipe(name: str, **kwargs: Any) -> CMakeGitRecipe: url=LIBRARIES_SOURCES_VERSION["pixman"][0], tag=LIBRARIES_SOURCES_VERSION["pixman"][1], source_folder=versioned_folder( - name="pixman", version=LIBRARIES_SOURCES_VERSION["pixman"][1].removeprefix("pixman-") + # str.removeprefix() is 3.9+; this project targets 3.8. + name="pixman", + version=( + LIBRARIES_SOURCES_VERSION["pixman"][1][len("pixman-") :] + if LIBRARIES_SOURCES_VERSION["pixman"][1].startswith("pixman-") + else LIBRARIES_SOURCES_VERSION["pixman"][1] + ), ), pkg_config_names=["pixman-1"], include_subdirs=["include", "include/pixman-1"], From 386a97bae74123d6e766e6eed286f1048bee0126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 01:21:56 -0500 Subject: [PATCH 3/9] Enhance type hinting in aliases.py and libraries.py - Added a future import for annotations in aliases.py to improve type hinting support. - Updated the import statement in libraries.py to use List from typing for better compatibility with Python versions prior to 3.9. --- orcsome3/aliases.py | 2 ++ orcsome3/libs/build/libraries.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/orcsome3/aliases.py b/orcsome3/aliases.py index 4ddc309..92948df 100644 --- a/orcsome3/aliases.py +++ b/orcsome3/aliases.py @@ -1,5 +1,7 @@ """Short key names mapped to X11 keysym names (`xev` / `X11/keysymdef.h` without the `XK_` prefix).""" +from __future__ import annotations + KEYS: dict[str, str] = { "`": "grave", "-": "minus", diff --git a/orcsome3/libs/build/libraries.py b/orcsome3/libs/build/libraries.py index 6497089..8d4e8b2 100644 --- a/orcsome3/libs/build/libraries.py +++ b/orcsome3/libs/build/libraries.py @@ -8,7 +8,7 @@ import traceback import urllib.request from pathlib import Path -from typing import Any, Callable, Optional, cast +from typing import Any, Callable, List, Optional, cast from setuptools import setup from setuptools.extension import Extension @@ -841,8 +841,10 @@ def _build() -> None: def _cythonize_extensions(module_list: list[Extension], **options: Any) -> list[Extension]: + # list[...] inside cast()'s first arg is evaluated at runtime (not deferred by `from __future__ + # import annotations`, unlike real annotations); bare generics need 3.9+, so use typing.List here. cythonize: Callable[..., list[Extension]] = cast( - Callable[..., list[Extension]], + Callable[..., List[Extension]], __import__(name="Cython.Build.Dependencies", fromlist=["cythonize"]).cythonize, ) return cythonize(module_list=module_list, **options) From c146c16b9850986d367897e563f09d9de67afb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 01:30:30 -0500 Subject: [PATCH 4/9] Update project metadata in pyproject.toml - Changed license specification to a string format and added license-files for clarity. - Removed redundant license classifier from classifiers list. - Expanded package list to include additional icon and library modules for better structure and organization. --- pyproject.toml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 388a0c0..431325e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,8 @@ build-backend = "setuptools.build_meta" [project] name = "orcsome3" -license = {file = "LICENSE"} +license = "MIT" +license-files = ["LICENSE"] authors = [ {name = "Ahsan Pérez", email = "ahsand.perez@gmail.com"} ] @@ -12,7 +13,6 @@ description = "Scripting extension for NETWM compliant window managers" requires-python = ">=3.8" classifiers = [ "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", "Development Status :: 4 - Beta", "Environment :: X11 Applications", "Topic :: Desktop Environment :: Window Managers", @@ -36,9 +36,19 @@ repository = "https://github.com/ahsand97/orcsome3" zip-safe = false packages = [ "orcsome3", + "orcsome3.icons", + "orcsome3.icons.hicolor", + "orcsome3.icons.hicolor.scalable", + "orcsome3.icons.hicolor.scalable.apps", "orcsome3.libs", "orcsome3.libs.build", "orcsome3.libs.cython_libs", + "orcsome3.libs.cython_libs.cairo", + "orcsome3.libs.cython_libs.imagemagick", + "orcsome3.libs.cython_libs.libev", + "orcsome3.libs.cython_libs.libgd", + "orcsome3.libs.cython_libs.resvg", + "orcsome3.libs.cython_libs.xlib", ] [tool.setuptools.cmdclass] From 2f71439f8555570b824165df4aa103fa3e8e7b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 01:40:30 -0500 Subject: [PATCH 5/9] Update license specification in pyproject.toml - Changed license format to use a file reference for better compatibility with setuptools. - Added a license classifier to the project metadata for clarity and compliance. --- pyproject.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 431325e..dbb6fdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,8 +4,10 @@ build-backend = "setuptools.build_meta" [project] name = "orcsome3" -license = "MIT" -license-files = ["LICENSE"] +# SPDX `license = "MIT"` needs setuptools>=77, which dropped Python 3.8 — and wheels-cp38 (CI) needs +# to keep building. Sticking with this older form means the "license as TOML table"/"license +# classifiers deprecated" warnings on newer setuptools are unavoidable until 3.8 support is dropped. +license = {file = "LICENSE"} authors = [ {name = "Ahsan Pérez", email = "ahsand.perez@gmail.com"} ] @@ -13,6 +15,7 @@ description = "Scripting extension for NETWM compliant window managers" requires-python = ">=3.8" classifiers = [ "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", "Development Status :: 4 - Beta", "Environment :: X11 Applications", "Topic :: Desktop Environment :: Window Managers", From b7c0436c59842dd5f52251da4ebe7082059450d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 01:53:07 -0500 Subject: [PATCH 6/9] Enhance Python publish workflow - Updated the artifact download steps in `python-publish.yml` to explicitly specify patterns for wheels and source distributions, preventing conflicts during the publish process. - Improved clarity in the workflow by adding comments regarding the artifact handling. --- .github/workflows/python-publish.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index a20d067..a23e825 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -110,13 +110,20 @@ jobs: name: pypi url: https://pypi.org/p/orcsome3 steps: + # Explicit names, not "download everything" — orcsome3-built-libraries (relayed to the wheel + # jobs) also exists in this run and would otherwise land in dist/ and break the publish step. - uses: actions/download-artifact@v8 with: + pattern: "wheels-*" path: dist merge-multiple: true + - uses: actions/download-artifact@v8 + with: + name: sdist + path: dist + - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: dist/ - password: ${{ secrets.PYPI_API_TOKEN }} From 19d4eeeeaf37a8b8fa324b1cdbffae0bb8fb390a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 02:03:51 -0500 Subject: [PATCH 7/9] Update license handling in pyproject.toml and python-publish.yml - Changed license specification in `pyproject.toml` to a string format and added `license-files` for improved compatibility with setuptools. - Updated the GitHub Actions workflow to patch the license format for builds using older setuptools versions, ensuring compatibility with Python 3.8. --- .github/workflows/python-publish.yml | 19 +++++++++++++++++++ pyproject.toml | 11 ++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index a23e825..6067018 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -59,6 +59,25 @@ jobs: with: persist-credentials: false + # setuptools>=77 (needed for the SPDX `license = "MIT"` form checked into pyproject.toml) has + # dropped Python 3.8; this job resolves an older setuptools that rejects that form outright. + # Patch just this job's checkout back to the old {file=...} form + classifier before building. + - name: Use classic license form (setuptools<77, no Python 3.8 support past that) + run: | + python3 -c " + content = open('pyproject.toml').read() + content = content.replace( + 'license = \"MIT\"\nlicense-files = [\"LICENSE\"]', + 'license = {file = \"LICENSE\"}', + ) + content = content.replace( + '\"Programming Language :: Python :: 3\",\n \"Development Status :: 4 - Beta\",', + '\"Programming Language :: Python :: 3\",\n \"License :: OSI Approved :: MIT License\",\n \"Development Status :: 4 - Beta\",', + ) + open('pyproject.toml', 'w').write(content) + " + grep -A1 "^license" pyproject.toml + # Static libs already built by `validate` (native, Linux glibc) — only linking happens here, # so no cmake/meson/rust toolchain is needed in this job. - uses: actions/download-artifact@v8 diff --git a/pyproject.toml b/pyproject.toml index dbb6fdd..60d6eb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,10 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "orcsome3" -# SPDX `license = "MIT"` needs setuptools>=77, which dropped Python 3.8 — and wheels-cp38 (CI) needs -# to keep building. Sticking with this older form means the "license as TOML table"/"license -# classifiers deprecated" warnings on newer setuptools are unavoidable until 3.8 support is dropped. -license = {file = "LICENSE"} +# SPDX form needs setuptools>=77, which dropped Python 3.8 — but wheels-cp38 (CI) resolves an older +# setuptools that rejects this form outright. python-publish.yml's wheels-cp38 job patches this file +# back to the old `{file=...}` form + classifier in its own checkout before building; every other +# consumer (this repo, validate, build_sdist, wheels-cp39-plus) uses the modern form below. +license = "MIT" +license-files = ["LICENSE"] authors = [ {name = "Ahsan Pérez", email = "ahsand.perez@gmail.com"} ] @@ -15,7 +17,6 @@ description = "Scripting extension for NETWM compliant window managers" requires-python = ">=3.8" classifiers = [ "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", "Development Status :: 4 - Beta", "Environment :: X11 Applications", "Topic :: Desktop Environment :: Window Managers", From 9032787d8eceeff417fb7be5e0b385f3b0988fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 02:15:29 -0500 Subject: [PATCH 8/9] Add enums for X11 constants and event types in orcsome3_backend.pyx - Introduced several IntEnum classes to represent X11 constants, event types, input event masks, key masks, button masks, and other related enums for better organization and clarity in the code. - Enhanced the structure of the backend module by categorizing constants and events, improving maintainability and readability. --- .../libs/cython_libs/orcsome3_backend.pyx | 489 +++++++++--------- 1 file changed, 245 insertions(+), 244 deletions(-) diff --git a/orcsome3/libs/cython_libs/orcsome3_backend.pyx b/orcsome3/libs/cython_libs/orcsome3_backend.pyx index 3d77c63..fb7c818 100644 --- a/orcsome3/libs/cython_libs/orcsome3_backend.pyx +++ b/orcsome3/libs/cython_libs/orcsome3_backend.pyx @@ -50,6 +50,251 @@ cdef void release_icon_data(IconData *icon_data): icon_data.length = 0 icon_data.magick_memory = False + +# Enums + +class CONSTANTS(pyenum.IntEnum): + CurrentTime = xlib.CurrentTime + AnyPropertyType = xlib.AnyPropertyType + NoSymbol = xlib.NoSymbol + AnyKey = xlib.AnyKey + XkbUseCoreKbd = xlib.XkbUseCoreKbd + RevertToNone = xlib.RevertToNone + RevertToPointerRoot = xlib.RevertToPointerRoot + RevertToParent = xlib.RevertToParent + +class EVENT_TYPES(pyenum.IntEnum): + # Generic event + GenericEvent = xlib.GenericEvent + # XErrorEvent + ErrorEvent = 0 + # XKeyEvent + KeyPress = xlib.KeyPress + KeyRelease = xlib.KeyRelease + # XButtonEvent + ButtonPress = xlib.ButtonPress + ButtonRelease = xlib.ButtonRelease + # XMotionEvent + MotionNotify = xlib.MotionNotify + # XCrossingEvent + EnterNotify = xlib.EnterNotify + LeaveNotify = xlib.LeaveNotify + # XFocusChangeEvent + FocusIn = xlib.FocusIn + FocusOut = xlib.FocusOut + # XExposeEvent + Expose = xlib.Expose + # XGraphicsExposeEvent + GraphicsExpose = xlib.GraphicsExpose + # XNoExposeEvent + NoExpose = xlib.NoExpose + # XVisibilityEvent + VisibilityNotify = xlib.VisibilityNotify + # XCreateWindowEvent + CreateNotify = xlib.CreateNotify + # XDestroyWindowEvent + DestroyNotify = xlib.DestroyNotify + # XUnmapEvent + UnmapNotify = xlib.UnmapNotify + # XMapEvent + MapNotify = xlib.MapNotify + # XMapRequestEvent + MapRequest = xlib.MapRequest + # XReparentEvent + ReparentNotify = xlib.ReparentNotify + # XConfigureEvent + ConfigureNotify = xlib.ConfigureNotify + # XGravityEvent + GravityNotify = xlib.GravityNotify + # XResizeRequestEvent + ResizeRequest = xlib.ResizeRequest + # XConfigureRequestEvent + ConfigureRequest = xlib.ConfigureRequest + # XCirculateEvent + CirculateNotify = xlib.CirculateNotify + # XCirculateRequestEvent + CirculateRequest = xlib.CirculateRequest + # XPropertyEvent + PropertyNotify = xlib.PropertyNotify + # XSelectionClearEvent + SelectionClear = xlib.SelectionClear + # XSelectionRequestEvent + SelectionRequest = xlib.SelectionRequest + # XSelectionEvent + SelectionNotify = xlib.SelectionNotify + # XColormapEvent + ColormapNotify = xlib.ColormapNotify + # XClientMessageEvent + ClientMessage = xlib.ClientMessage + # XMappingEvent + MappingNotify = xlib.MappingNotify + # XKeymapEvent + KeymapNotify = xlib.KeymapNotify + +class INPUT_EVENT_MASKS(pyenum.IntEnum): + NoEventMask = xlib.NoEventMask + StructureNotifyMask = xlib.StructureNotifyMask + SubstructureNotifyMask = xlib.SubstructureNotifyMask + SubstructureRedirectMask = xlib.SubstructureRedirectMask + PropertyChangeMask = xlib.PropertyChangeMask + FocusChangeMask = xlib.FocusChangeMask + KeyPressMask = xlib.KeyPressMask + KeyReleaseMask = xlib.KeyReleaseMask + ButtonPressMask = xlib.ButtonPressMask + ButtonReleaseMask = xlib.ButtonReleaseMask + +class KEY_MASKS(pyenum.IntEnum): + AnyModifier = xlib.AnyModifier + Mod1Mask = xlib.Mod1Mask + ControlMask = xlib.ControlMask + ShiftMask = xlib.ShiftMask + Mod2Mask = xlib.Mod2Mask + Mod4Mask = xlib.Mod4Mask + LockMask = xlib.LockMask + +class BUTTON_MASKS(pyenum.IntEnum): + AnyModifier = xlib.AnyModifier + Button1Mask = xlib.Button1Mask + Button2Mask = xlib.Button2Mask + Button3Mask = xlib.Button3Mask + Button4Mask = xlib.Button4Mask + Button5Mask = xlib.Button5Mask + +class BUTTONS(pyenum.IntEnum): + AnyButton = xlib.AnyButton + Button1 = xlib.Button1 + Button2 = xlib.Button2 + Button3 = xlib.Button3 + Button4 = xlib.Button4 + Button5 = xlib.Button5 + +class GRAB_MODE(pyenum.IntEnum): + GrabModeSync = xlib.GrabModeSync + GrabModeAsync = xlib.GrabModeAsync + +class WINDOW_VALUE_MASK(pyenum.IntEnum): + CWX = xlib.CWX + CWY = xlib.CWY + CWWidth = xlib.CWWidth + CWHeight = xlib.CWHeight + CWBorderWidth = xlib.CWBorderWidth + CWSibling = xlib.CWSibling + CWStackMode = xlib.CWStackMode + +class WINDOW_STACKING_METHOD(pyenum.IntEnum): + Above = xlib.Above + Below = xlib.Below + TopIf = xlib.TopIf + BottomIf = xlib.BottomIf + Opposite = xlib.Opposite + +class SET_PROPERTY_MODE(pyenum.IntEnum): + PropModeReplace = xlib.PropModeReplace + PropModePrepend = xlib.PropModePrepend + PropModeAppend = xlib.PropModeAppend + +class WINDOW_MAP_STATE(pyenum.IntEnum): + IsUnmapped = xlib.IsUnmapped + IsUnviewable = xlib.IsUnviewable + IsViewable = xlib.IsViewable + +class SCREENSAVER_STATE(pyenum.IntEnum): + ScreenSaverOff = xlib.ScreenSaverOff + ScreenSaverOn = xlib.ScreenSaverOn + ScreenSaverCycle = xlib.ScreenSaverCycle + ScreenSaverDisabled = xlib.ScreenSaverDisabled + +class SCREENSAVER_KIND(pyenum.IntEnum): + ScreenSaverBlanked = xlib.ScreenSaverBlanked + ScreenSaverInternal = xlib.ScreenSaverInternal + ScreenSaverExternal = xlib.ScreenSaverExternal + +class KB_GROUP_INDEX(pyenum.IntEnum): + XkbGroup1Index = xlib.XkbGroup1Index + XkbGroup2Index = xlib.XkbGroup2Index + XkbGroup3Index = xlib.XkbGroup3Index + XkbGroup4Index = xlib.XkbGroup4Index + XkbAnyGroup = xlib.XkbAnyGroup + XkbAllGroups = xlib.XkbAllGroups + +class NOTIFY_MODES(pyenum.IntEnum): + NotifyNormal = xlib.NotifyNormal + NotifyGrab = xlib.NotifyGrab + NotifyUngrab = xlib.NotifyUngrab + NotifyWhileGrabbed = xlib.NotifyWhileGrabbed + +class NOTIFY_DETAILS(pyenum.IntEnum): + NotifyAncestor = xlib.NotifyAncestor + NotifyVirtual = xlib.NotifyVirtual + NotifyInferior = xlib.NotifyInferior + NotifyNonlinear = xlib.NotifyNonlinear + NotifyNonlinearVirtual = xlib.NotifyNonlinearVirtual + NotifyPointer = xlib.NotifyPointer + NotifyPointerRoot = xlib.NotifyPointerRoot + NotifyDetailNone = xlib.NotifyDetailNone + +class PROPERTY_NOTIFICATION(pyenum.IntEnum): + PropertyNewValue = xlib.PropertyNewValue + PropertyDelete = xlib.PropertyDelete + +class PROPERTY_FORMAT(pyenum.Enum): + """ + Enum representing the valid formats when getting/setting a property. + + - CHAR: Format with value 8. The value of the property is a char array + - SHORT: Format with value 16. The value of the property is a short array + - LONG: Format with value 32. The value of the property is a long array + """ + + CHAR = (8, "b") + SHORT = (16, "h") + LONG = (32, "l") + + @classmethod + def new_from_value(cls, value: Union[str, int]) -> "PROPERTY_FORMAT": + for member in cls: + if value in member.value: + return member + raise ValueError(f"The value {value} is not in the enum members") + +class DPMS_POWER_LEVEL(pyenum.IntEnum): + DPMSModeOn = xlib.DPMSModeOn + DPMSModeStandby = xlib.DPMSModeStandby + DPMSModeSuspend = xlib.DPMSModeSuspend + DPMSModeOff = xlib.DPMSModeOff + +class PYLOOP_NEW_LOOP_FLAGS(pyenum.IntEnum): + EVFLAG_AUTO = libev.EVFLAG_AUTO + EVFLAG_NOENV = libev.EVFLAG_NOENV + EVFLAG_FORKCHECK = libev.EVFLAG_FORKCHECK + EVFLAG_NOINOTIFY = libev.EVFLAG_NOINOTIFY + EVFLAG_SIGNALFD = libev.EVFLAG_SIGNALFD + EVFLAG_NOSIGMASK = libev.EVFLAG_NOSIGMASK + EVBACKEND_SELECT = libev.EVBACKEND_SELECT + EVBACKEND_POLL = libev.EVBACKEND_POLL + EVBACKEND_EPOLL = libev.EVBACKEND_EPOLL + EVBACKEND_KQUEUE = libev.EVBACKEND_KQUEUE + EVBACKEND_DEVPOLL = libev.EVBACKEND_DEVPOLL + EVBACKEND_PORT = libev.EVBACKEND_PORT + EVBACKEND_ALL = libev.EVBACKEND_ALL + EVBACKEND_MASK = libev.EVBACKEND_MASK + +class PYLOOP_RUN_LOOP_FLAGS(pyenum.IntEnum): + EVRUN_ALWAYS = 0 # Keep handling events until either no event watchers are active anymore or "ev_break" was called + EVRUN_ONCE = libev.EVRUN_ONCE + EVRUN_NOWAIT = libev.EVRUN_NOWAIT + +class PYLOOP_BREAK_LOOP_FLAGS(pyenum.IntEnum): + EVBREAK_ALL = libev.EVBREAK_ALL + EVBREAK_ONE = libev.EVBREAK_ONE + EVBREAK_CANCEL = libev.EVBREAK_CANCEL + +class PYIOWATCHER_INIT_FLAGS(pyenum.IntEnum): + EV_READ = libev.EV_READ + EV_WRITE = libev.EV_WRITE + EV_READ_WRITE = libev.EV_READ | libev.EV_WRITE + + cdef class PyDisplay: cdef xlib.Display *_display @@ -1168,250 +1413,6 @@ cdef class PyStatWatcher: ) -# Enums - -class CONSTANTS(pyenum.IntEnum): - CurrentTime = xlib.CurrentTime - AnyPropertyType = xlib.AnyPropertyType - NoSymbol = xlib.NoSymbol - AnyKey = xlib.AnyKey - XkbUseCoreKbd = xlib.XkbUseCoreKbd - RevertToNone = xlib.RevertToNone - RevertToPointerRoot = xlib.RevertToPointerRoot - RevertToParent = xlib.RevertToParent - -class EVENT_TYPES(pyenum.IntEnum): - # Generic event - GenericEvent = xlib.GenericEvent - # XErrorEvent - ErrorEvent = 0 - # XKeyEvent - KeyPress = xlib.KeyPress - KeyRelease = xlib.KeyRelease - # XButtonEvent - ButtonPress = xlib.ButtonPress - ButtonRelease = xlib.ButtonRelease - # XMotionEvent - MotionNotify = xlib.MotionNotify - # XCrossingEvent - EnterNotify = xlib.EnterNotify - LeaveNotify = xlib.LeaveNotify - # XFocusChangeEvent - FocusIn = xlib.FocusIn - FocusOut = xlib.FocusOut - # XExposeEvent - Expose = xlib.Expose - # XGraphicsExposeEvent - GraphicsExpose = xlib.GraphicsExpose - # XNoExposeEvent - NoExpose = xlib.NoExpose - # XVisibilityEvent - VisibilityNotify = xlib.VisibilityNotify - # XCreateWindowEvent - CreateNotify = xlib.CreateNotify - # XDestroyWindowEvent - DestroyNotify = xlib.DestroyNotify - # XUnmapEvent - UnmapNotify = xlib.UnmapNotify - # XMapEvent - MapNotify = xlib.MapNotify - # XMapRequestEvent - MapRequest = xlib.MapRequest - # XReparentEvent - ReparentNotify = xlib.ReparentNotify - # XConfigureEvent - ConfigureNotify = xlib.ConfigureNotify - # XGravityEvent - GravityNotify = xlib.GravityNotify - # XResizeRequestEvent - ResizeRequest = xlib.ResizeRequest - # XConfigureRequestEvent - ConfigureRequest = xlib.ConfigureRequest - # XCirculateEvent - CirculateNotify = xlib.CirculateNotify - # XCirculateRequestEvent - CirculateRequest = xlib.CirculateRequest - # XPropertyEvent - PropertyNotify = xlib.PropertyNotify - # XSelectionClearEvent - SelectionClear = xlib.SelectionClear - # XSelectionRequestEvent - SelectionRequest = xlib.SelectionRequest - # XSelectionEvent - SelectionNotify = xlib.SelectionNotify - # XColormapEvent - ColormapNotify = xlib.ColormapNotify - # XClientMessageEvent - ClientMessage = xlib.ClientMessage - # XMappingEvent - MappingNotify = xlib.MappingNotify - # XKeymapEvent - KeymapNotify = xlib.KeymapNotify - -class INPUT_EVENT_MASKS(pyenum.IntEnum): - NoEventMask = xlib.NoEventMask - StructureNotifyMask = xlib.StructureNotifyMask - SubstructureNotifyMask = xlib.SubstructureNotifyMask - SubstructureRedirectMask = xlib.SubstructureRedirectMask - PropertyChangeMask = xlib.PropertyChangeMask - FocusChangeMask = xlib.FocusChangeMask - KeyPressMask = xlib.KeyPressMask - KeyReleaseMask = xlib.KeyReleaseMask - ButtonPressMask = xlib.ButtonPressMask - ButtonReleaseMask = xlib.ButtonReleaseMask - -class KEY_MASKS(pyenum.IntEnum): - AnyModifier = xlib.AnyModifier - Mod1Mask = xlib.Mod1Mask - ControlMask = xlib.ControlMask - ShiftMask = xlib.ShiftMask - Mod2Mask = xlib.Mod2Mask - Mod4Mask = xlib.Mod4Mask - LockMask = xlib.LockMask - -class BUTTON_MASKS(pyenum.IntEnum): - AnyModifier = xlib.AnyModifier - Button1Mask = xlib.Button1Mask - Button2Mask = xlib.Button2Mask - Button3Mask = xlib.Button3Mask - Button4Mask = xlib.Button4Mask - Button5Mask = xlib.Button5Mask - -class BUTTONS(pyenum.IntEnum): - AnyButton = xlib.AnyButton - Button1 = xlib.Button1 - Button2 = xlib.Button2 - Button3 = xlib.Button3 - Button4 = xlib.Button4 - Button5 = xlib.Button5 - -class GRAB_MODE(pyenum.IntEnum): - GrabModeSync = xlib.GrabModeSync - GrabModeAsync = xlib.GrabModeAsync - -class WINDOW_VALUE_MASK(pyenum.IntEnum): - CWX = xlib.CWX - CWY = xlib.CWY - CWWidth = xlib.CWWidth - CWHeight = xlib.CWHeight - CWBorderWidth = xlib.CWBorderWidth - CWSibling = xlib.CWSibling - CWStackMode = xlib.CWStackMode - -class WINDOW_STACKING_METHOD(pyenum.IntEnum): - Above = xlib.Above - Below = xlib.Below - TopIf = xlib.TopIf - BottomIf = xlib.BottomIf - Opposite = xlib.Opposite - -class SET_PROPERTY_MODE(pyenum.IntEnum): - PropModeReplace = xlib.PropModeReplace - PropModePrepend = xlib.PropModePrepend - PropModeAppend = xlib.PropModeAppend - -class WINDOW_MAP_STATE(pyenum.IntEnum): - IsUnmapped = xlib.IsUnmapped - IsUnviewable = xlib.IsUnviewable - IsViewable = xlib.IsViewable - -class SCREENSAVER_STATE(pyenum.IntEnum): - ScreenSaverOff = xlib.ScreenSaverOff - ScreenSaverOn = xlib.ScreenSaverOn - ScreenSaverCycle = xlib.ScreenSaverCycle - ScreenSaverDisabled = xlib.ScreenSaverDisabled - -class SCREENSAVER_KIND(pyenum.IntEnum): - ScreenSaverBlanked = xlib.ScreenSaverBlanked - ScreenSaverInternal = xlib.ScreenSaverInternal - ScreenSaverExternal = xlib.ScreenSaverExternal - -class KB_GROUP_INDEX(pyenum.IntEnum): - XkbGroup1Index = xlib.XkbGroup1Index - XkbGroup2Index = xlib.XkbGroup2Index - XkbGroup3Index = xlib.XkbGroup3Index - XkbGroup4Index = xlib.XkbGroup4Index - XkbAnyGroup = xlib.XkbAnyGroup - XkbAllGroups = xlib.XkbAllGroups - -class NOTIFY_MODES(pyenum.IntEnum): - NotifyNormal = xlib.NotifyNormal - NotifyGrab = xlib.NotifyGrab - NotifyUngrab = xlib.NotifyUngrab - NotifyWhileGrabbed = xlib.NotifyWhileGrabbed - -class NOTIFY_DETAILS(pyenum.IntEnum): - NotifyAncestor = xlib.NotifyAncestor - NotifyVirtual = xlib.NotifyVirtual - NotifyInferior = xlib.NotifyInferior - NotifyNonlinear = xlib.NotifyNonlinear - NotifyNonlinearVirtual = xlib.NotifyNonlinearVirtual - NotifyPointer = xlib.NotifyPointer - NotifyPointerRoot = xlib.NotifyPointerRoot - NotifyDetailNone = xlib.NotifyDetailNone - -class PROPERTY_NOTIFICATION(pyenum.IntEnum): - PropertyNewValue = xlib.PropertyNewValue - PropertyDelete = xlib.PropertyDelete - -class PROPERTY_FORMAT(pyenum.Enum): - """ - Enum representing the valid formats when getting/setting a property. - - - CHAR: Format with value 8. The value of the property is a char array - - SHORT: Format with value 16. The value of the property is a short array - - LONG: Format with value 32. The value of the property is a long array - """ - - CHAR = (8, "b") - SHORT = (16, "h") - LONG = (32, "l") - - @classmethod - def new_from_value(cls, value: Union[str, int]) -> "PROPERTY_FORMAT": - for member in cls: - if value in member.value: - return member - raise ValueError(f"The value {value} is not in the enum members") - -class DPMS_POWER_LEVEL(pyenum.IntEnum): - DPMSModeOn = xlib.DPMSModeOn - DPMSModeStandby = xlib.DPMSModeStandby - DPMSModeSuspend = xlib.DPMSModeSuspend - DPMSModeOff = xlib.DPMSModeOff - -class PYLOOP_NEW_LOOP_FLAGS(pyenum.IntEnum): - EVFLAG_AUTO = libev.EVFLAG_AUTO - EVFLAG_NOENV = libev.EVFLAG_NOENV - EVFLAG_FORKCHECK = libev.EVFLAG_FORKCHECK - EVFLAG_NOINOTIFY = libev.EVFLAG_NOINOTIFY - EVFLAG_SIGNALFD = libev.EVFLAG_SIGNALFD - EVFLAG_NOSIGMASK = libev.EVFLAG_NOSIGMASK - EVBACKEND_SELECT = libev.EVBACKEND_SELECT - EVBACKEND_POLL = libev.EVBACKEND_POLL - EVBACKEND_EPOLL = libev.EVBACKEND_EPOLL - EVBACKEND_KQUEUE = libev.EVBACKEND_KQUEUE - EVBACKEND_DEVPOLL = libev.EVBACKEND_DEVPOLL - EVBACKEND_PORT = libev.EVBACKEND_PORT - EVBACKEND_ALL = libev.EVBACKEND_ALL - EVBACKEND_MASK = libev.EVBACKEND_MASK - -class PYLOOP_RUN_LOOP_FLAGS(pyenum.IntEnum): - EVRUN_ALWAYS = 0 # Keep handling events until either no event watchers are active anymore or "ev_break" was called - EVRUN_ONCE = libev.EVRUN_ONCE - EVRUN_NOWAIT = libev.EVRUN_NOWAIT - -class PYLOOP_BREAK_LOOP_FLAGS(pyenum.IntEnum): - EVBREAK_ALL = libev.EVBREAK_ALL - EVBREAK_ONE = libev.EVBREAK_ONE - EVBREAK_CANCEL = libev.EVBREAK_CANCEL - -class PYIOWATCHER_INIT_FLAGS(pyenum.IntEnum): - EV_READ = libev.EV_READ - EV_WRITE = libev.EV_WRITE - EV_READ_WRITE = libev.EV_READ | libev.EV_WRITE - - # Functions def PyXOpenDisplay(display_name: Optional[str]) -> Optional[PyDisplay]: From f768d6b74c37a173344a107444a0aac8cd63e487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahsan=20P=C3=A9rez?= Date: Mon, 31 Aug 2026 02:39:53 -0500 Subject: [PATCH 9/9] Update Python publish workflow to conditionally publish releases - Added a conditional check in `python-publish.yml` to ensure that a release is published only when triggered by a release event or from the master branch, enhancing the workflow's reliability and preventing unintended publishes from feature branches. --- .github/workflows/python-publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 6067018..2cc5b90 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -122,6 +122,9 @@ jobs: - wheels-cp39-plus - wheels-cp38 - build_sdist + # A real release always publishes (github.ref is the tag, e.g. refs/tags/v1.0.0, not a branch); + # a manual workflow_dispatch only publishes when run from master, not a stray feature branch. + if: github.event_name == 'release' || github.ref == 'refs/heads/master' runs-on: ubuntu-latest permissions: id-token: write