├── .github └── workflows │ └── pythonpublish.yml ├── .gitignore ├── LICENSE ├── README.md ├── example └── dump_devices.py ├── setup.py └── src └── cuesdk ├── __init__.py ├── api.py ├── bin ├── iCUESDK.x64_2019.dll └── libiCUESDK.dylib ├── enums.py ├── helpers.py ├── native ├── __init__.py ├── capi.py └── structs.py ├── structs.py └── version.py /.github/workflows/pythonpublish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: '>=3.9' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | python -m pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.pypi_usr }} 28 | TWINE_PASSWORD: ${{ secrets.pypi_pwd }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Visual Studio Code 132 | .vscode/ 133 | 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Corsair Memory, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cue-sdk-python 2 | 3 | [![PyPI license](https://img.shields.io/pypi/l/cuesdk.svg?style=for-the-badge)](https://pypi.org/project/cuesdk) 4 | [![PyPI version info](https://img.shields.io/pypi/v/cuesdk.svg?style=for-the-badge)](https://pypi.org/project/cuesdk) 5 | [![PyPI supported Python versions](https://img.shields.io/pypi/pyversions/cuesdk.svg?style=for-the-badge)](https://pypi.org/project/cuesdk) 6 | 7 | ## Intro 8 | 9 | This repository is dedicated for a `cuesdk` package on [PyPI](https://pypi.org/project/cuesdk) 10 | 11 | `cuesdk` package is a `ctypes`-based CUE SDK binding for Python 3 12 | 13 | ## Requirements 14 | 15 | `cuesdk` can be used on the following platforms: 16 | 17 | - Windows 7 (x64); 18 | - Windows 8, 8.1 (x64); 19 | - Windows 10 (x64); 20 | - Windows 11 (x64); 21 | - macOS 10.13; 22 | - macOS 10.14; 23 | - macOS 10.15; 24 | - macOS 11; 25 | 26 | ## Prerequisites 27 | 28 | - Python 3.9 or higher. Support for earlier versions of Python is not provided. Python 2.7 or lower is not supported. 29 | 30 | ### Windows 31 | 32 | - [iCUE for Windows](https://www.corsair.com/icue) 33 | - Microsoft Visual C++ Redistributable for Visual Studio 2019. 34 | - x64 35 | 36 | ### macOS 37 | 38 | - [iCUE for macOS](https://www.corsair.com/icue-mac) 39 | 40 | ## Installing 41 | 42 | You can install the package from [PyPI](https://pypi.org/project/cuesdk): 43 | 44 | ```sh 45 | # Windows 46 | $ py -3 -m pip install -U cuesdk 47 | ``` 48 | 49 | ```sh 50 | # macOS 51 | $ python3 -m pip install -U cuesdk 52 | ``` 53 | 54 | ## Usage 55 | 56 | ```python 57 | from cuesdk import (CueSdk, CorsairDeviceFilter, CorsairDeviceType, CorsairError) 58 | 59 | sdk = CueSdk() 60 | 61 | def on_state_changed(evt): 62 | print(evt.state) 63 | 64 | err = sdk.connect(on_state_changed) 65 | 66 | details, err = sdk.get_session_details() 67 | print(details) 68 | 69 | devices, err = sdk.get_devices( 70 | CorsairDeviceFilter(device_type_mask=CorsairDeviceType.CDT_Keyboard)) 71 | if err == CorsairError.CE_Success: 72 | for d in devices: 73 | print(sdk.get_device_info(d.device_id)) 74 | 75 | ``` 76 | -------------------------------------------------------------------------------- /example/dump_devices.py: -------------------------------------------------------------------------------- 1 | from cuesdk import (CueSdk, CorsairDeviceFilter, CorsairDeviceType, 2 | CorsairError, CorsairSessionState, CorsairDeviceInfo) 3 | import sys 4 | 5 | COLS = ["Device type", "Model name", "LED count", "Channel count"] 6 | COL_WIDTH = 28 7 | 8 | 9 | def hr(): 10 | print("—" * COL_WIDTH * len(COLS)) 11 | 12 | 13 | def print_device(device: CorsairDeviceInfo): 14 | print(f"{str(device.type)[18:]:{COL_WIDTH-2}} |" 15 | f" {device.model:{COL_WIDTH-3}} |" 16 | f" {device.led_count:{COL_WIDTH-3}} |" 17 | f" {device.channel_count:{COL_WIDTH-2}}") 18 | 19 | 20 | def main(): 21 | sdk = CueSdk() 22 | 23 | def on_state_changed(evt): 24 | print(evt.state) 25 | # the app must wait for CSS_Connected event before proceeding 26 | if evt.state == CorsairSessionState.CSS_Connected: 27 | details, err = sdk.get_session_details() 28 | print(details) 29 | 30 | devices, err = sdk.get_devices( 31 | CorsairDeviceFilter( 32 | device_type_mask=CorsairDeviceType.CDT_All)) 33 | if err == CorsairError.CE_Success and devices: 34 | hr() 35 | print("|".join([f"{col:^{COL_WIDTH-1}}" for col in COLS])) 36 | hr() 37 | for d in devices: 38 | device, err = sdk.get_device_info(d.device_id) 39 | if device: 40 | print_device(device) 41 | hr() 42 | else: 43 | print(err) 44 | 45 | err = sdk.connect(on_state_changed) 46 | if err != CorsairError.CE_Success: 47 | print("\nERROR: Unable to connect to iCUE") 48 | print(err) 49 | sys.exit() 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | input() # wait for 55 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import re 4 | from setuptools import setup, find_packages 5 | 6 | 7 | def read_version(filename='src/cuesdk/version.py'): 8 | """Parse a __version__ number from a source file""" 9 | with open(filename) as source: 10 | text = source.read() 11 | match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", text) 12 | if not match: 13 | msg = 'Unable to find version number in {}'.format(filename) 14 | raise RuntimeError(msg) 15 | version = match.group(1) 16 | return version 17 | 18 | 19 | system = platform.system().lower() 20 | 21 | if system not in ('windows', 'darwin'): 22 | msg = '{} system is not supported'.format(system) 23 | raise RuntimeError(msg) 24 | 25 | 26 | def package_files(directory): 27 | return [ 28 | os.path.join('..', '..', path, filename) 29 | for (path, directories, filenames) in os.walk(directory) 30 | for filename in filenames 31 | ] 32 | 33 | 34 | setup(name="cuesdk", 35 | version=read_version(), 36 | packages=find_packages('src'), 37 | package_dir={'': 'src'}, 38 | package_data={ 39 | 'cuesdk': package_files('src/cuesdk/bin'), 40 | }, 41 | zip_safe=False, 42 | author='Corsair Memory, Inc.', 43 | license='MIT', 44 | url='https://github.com/CorsairOfficial/cue-sdk-python', 45 | description='Ctypes-based CUE SDK binding for Python', 46 | long_description=open('README.md').read(), 47 | long_description_content_type='text/markdown', 48 | install_requires=[], 49 | python_requires='>=3.9', 50 | classifiers=[ 51 | 'Development Status :: 5 - Production/Stable', 52 | 'Intended Audience :: Developers', 53 | 'Intended Audience :: End Users/Desktop', 54 | 'License :: OSI Approved :: MIT License', 55 | 'Operating System :: MacOS', 56 | 'Operating System :: MacOS :: MacOS X', 57 | 'Operating System :: Microsoft :: Windows', 58 | 'Programming Language :: Python :: 3.9', 59 | 'Programming Language :: Python :: 3.10', 60 | 'Programming Language :: Python :: Implementation :: CPython', 61 | 'Topic :: Games/Entertainment', 62 | 'Topic :: System :: Hardware' 63 | ]) 64 | -------------------------------------------------------------------------------- /src/cuesdk/__init__.py: -------------------------------------------------------------------------------- 1 | from .enums import * 2 | from .structs import * 3 | from .api import * 4 | -------------------------------------------------------------------------------- /src/cuesdk/api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | from ctypes import (c_int32, c_uint32, c_void_p, byref, sizeof, 4 | create_string_buffer) 5 | from typing import (Any, Collection, Sequence, Optional, Callable) 6 | 7 | from .enums import (CorsairAccessLevel, CorsairDataType, CorsairError, 8 | CorsairDevicePropertyId) 9 | from .structs import (CorsairDeviceFilter, CorsairEvent, CorsairProperty, 10 | CorsairKeyEventConfiguration, CorsairLedPosition, 11 | CorsairLedColor, CorsairDeviceInfo, 12 | CorsairSessionDetails, CorsairSessionStateChanged) 13 | from .native import ( 14 | CorsairNativeApi, CorsairSessionStateChangedHandler, CorsairEventHandler, 15 | CorsairAsyncCallback, CORSAIR_STRING_SIZE_M, CORSAIR_DEVICE_COUNT_MAX, 16 | CORSAIR_DEVICE_LEDCOUNT_MAX, CORSAIR_LAYER_PRIORITY_MAX, 17 | CorsairSessionDetails as CorsairSessionDetailsNative, CorsairDeviceInfo as 18 | CorsairDeviceInfoNative, CorsairDeviceFilter as CorsairDeviceFilterNative, 19 | CorsairLedPosition as CorsairLedPositionNative, 20 | CorsairKeyEventConfiguration as CorsairKeyEventConfigurationNative, 21 | CorsairProperty as CorsairPropertyNative, CorsairLedColor as 22 | CorsairLedColorNative) 23 | 24 | __all__ = ['CueSdk'] 25 | 26 | napi: CorsairNativeApi 27 | 28 | 29 | def get_library_path(lib_name): 30 | return os.path.join(os.path.dirname(__file__), 'bin', lib_name) 31 | 32 | 33 | def get_library_path_windows(): 34 | lib_name = 'iCUESDK.x64_2019.dll' 35 | return get_library_path(lib_name) 36 | 37 | 38 | def get_library_path_mac(): 39 | lib_name = 'libiCUESDK.dylib' 40 | return get_library_path(lib_name) 41 | 42 | 43 | def str_to_char_array(s: str, sz: int): 44 | return create_string_buffer(s.encode('utf-8'), sz) 45 | 46 | 47 | def to_native_id(device_id): 48 | if not device_id: 49 | return None 50 | return str_to_char_array(device_id, CORSAIR_STRING_SIZE_M) 51 | 52 | 53 | class CueSdk(object): 54 | 55 | def __init__(self, sdk_path: Optional[str] = None) -> None: 56 | global napi 57 | if sdk_path is None: 58 | system = platform.system() 59 | if system == "Windows": 60 | sdk_path = get_library_path_windows() 61 | elif system == "Darwin": 62 | sdk_path = get_library_path_mac() 63 | napi = CorsairNativeApi(sdk_path) 64 | self._protocol_details = None 65 | 66 | def __enter__(self): 67 | return self 68 | 69 | def __exit__(self, type, value, traceback): 70 | pass 71 | 72 | def connect( 73 | self, on_state_changed: Callable[[CorsairSessionStateChanged], None] 74 | ) -> CorsairError: 75 | if sizeof(c_void_p) < 8: 76 | return CorsairError(CorsairError.CE_NotAllowed) 77 | if on_state_changed is None: 78 | return CorsairError(CorsairError.CE_InvalidArguments) 79 | 80 | def raw_handler(ctx, e): 81 | evt = CorsairSessionStateChanged.create(e.contents) 82 | on_state_changed(evt) 83 | 84 | self.session_state_changed_event_handler = CorsairSessionStateChangedHandler( 85 | raw_handler) 86 | 87 | return CorsairError( 88 | napi.CorsairConnect(self.session_state_changed_event_handler, 89 | None)) 90 | 91 | def disconnect(self) -> CorsairError: 92 | self.session_state_changed_event_handler = None 93 | return CorsairError(napi.CorsairDisconnect()) 94 | 95 | def get_session_details(self): 96 | res = None 97 | nobj = CorsairSessionDetailsNative() 98 | err = CorsairError(napi.CorsairGetSessionDetails(nobj)) 99 | if err == CorsairError.CE_Success: 100 | res = CorsairSessionDetails.create(nobj) 101 | return (res, err) 102 | 103 | def get_devices(self, device_filter: CorsairDeviceFilter): 104 | if not device_filter: 105 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 106 | 107 | df = CorsairDeviceFilterNative( 108 | deviceTypeMask=device_filter.device_type_mask) 109 | 110 | infos = (CorsairDeviceInfoNative * CORSAIR_DEVICE_COUNT_MAX)() 111 | cnt = c_int32() 112 | err = CorsairError( 113 | napi.CorsairGetDevices(df, CORSAIR_DEVICE_COUNT_MAX, infos, 114 | byref(cnt))) 115 | 116 | if err == CorsairError.CE_Success: 117 | return ([ 118 | CorsairDeviceInfo.create(infos[i]) for i in range(cnt.value) 119 | ], err) 120 | 121 | return (None, err) 122 | 123 | def get_device_info(self, device_id: str): 124 | if not device_id: 125 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 126 | 127 | nobj = CorsairDeviceInfoNative() 128 | err = CorsairError( 129 | napi.CorsairGetDeviceInfo(to_native_id(device_id), nobj)) 130 | if err == CorsairError.CE_Success: 131 | return (CorsairDeviceInfo.create(nobj), err) 132 | return (None, err) 133 | 134 | def get_led_positions(self, device_id: str): 135 | if not device_id: 136 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 137 | 138 | leds = (CorsairLedPositionNative * CORSAIR_DEVICE_LEDCOUNT_MAX)() 139 | cnt = c_int32() 140 | err = CorsairError( 141 | napi.CorsairGetLedPositions(to_native_id(device_id), 142 | CORSAIR_DEVICE_LEDCOUNT_MAX, leds, 143 | byref(cnt))) 144 | 145 | if err == CorsairError.CE_Success: 146 | return ([ 147 | CorsairLedPosition.create(leds[i]) for i in range(cnt.value) 148 | ], err) 149 | 150 | return (None, err) 151 | 152 | def subscribe_for_events( 153 | self, on_event: Callable[[CorsairEvent], None]) -> CorsairError: 154 | if on_event is None: 155 | return CorsairError(CorsairError.CE_InvalidArguments) 156 | 157 | def raw_handler(ctx, e): 158 | evt = CorsairEvent.create(e.contents) 159 | on_event(evt) 160 | 161 | self.event_handler = CorsairEventHandler(raw_handler) 162 | return CorsairError( 163 | napi.CorsairSubscribeForEvents(self.event_handler, None)) 164 | 165 | def unsubscribe_from_events(self) -> CorsairError: 166 | self.event_handler = None 167 | return CorsairError(napi.CorsairUnsubscribeFromEvents()) 168 | 169 | def configure_key_event( 170 | self, device_id: str, 171 | configuration: CorsairKeyEventConfiguration) -> CorsairError: 172 | if not device_id or not configuration: 173 | return CorsairError(CorsairError.CE_InvalidArguments) 174 | 175 | cfg = CorsairKeyEventConfigurationNative() 176 | cfg.keyId = configuration.key_id 177 | cfg.isIntercepted = configuration.is_intercepted 178 | return CorsairError( 179 | napi.CorsairConfigureKeyEvent(to_native_id(device_id), cfg)) 180 | 181 | def get_device_property_info(self, 182 | device_id: str, 183 | property_id: CorsairDevicePropertyId, 184 | index: int = 0): 185 | if not device_id or not property_id or index < 0: 186 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 187 | 188 | dt = c_uint32() 189 | flags = c_uint32() 190 | err = CorsairError( 191 | napi.CorsairGetDevicePropertyInfo(to_native_id(device_id), 192 | property_id, index, byref(dt), 193 | byref(flags))) 194 | 195 | res = None 196 | if err == CorsairError.CE_Success: 197 | res = {'data_type': CorsairDataType(dt.value), 'flags': flags.value} 198 | 199 | return (res, err) 200 | 201 | def read_device_property(self, 202 | device_id: str, 203 | property_id: CorsairDevicePropertyId, 204 | index: int = 0): 205 | if not device_id or not property_id or index < 0: 206 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 207 | 208 | nobj = CorsairPropertyNative() 209 | err = CorsairError( 210 | napi.CorsairReadDeviceProperty(to_native_id(device_id), 211 | property_id, index, nobj)) 212 | 213 | if err == CorsairError.CE_Success: 214 | return (CorsairProperty.create(nobj), err) 215 | 216 | return (None, err) 217 | 218 | def write_device_property(self, device_id: str, 219 | property_id: CorsairDevicePropertyId, index: int, 220 | prop: Any) -> CorsairError: 221 | if not device_id or not property_id or index < 0 or not prop: 222 | return CorsairError(CorsairError.CE_InvalidArguments) 223 | 224 | nobj = CorsairPropertyNative() 225 | nobj.type = prop.type 226 | nobj.value = prop.value # TODO: convert value to native object 227 | 228 | return CorsairError( 229 | napi.CorsairWriteDeviceProperty(to_native_id(device_id), 230 | property_id, index, nobj)) 231 | 232 | def request_control(self, device_id: str, 233 | access_level: CorsairAccessLevel) -> CorsairError: 234 | return CorsairError( 235 | napi.CorsairRequestControl(to_native_id(device_id), access_level)) 236 | 237 | def release_control(self, device_id: Optional[str]) -> CorsairError: 238 | return CorsairError(napi.CorsairReleaseControl( 239 | to_native_id(device_id))) 240 | 241 | def set_layer_priority(self, priority: int) -> CorsairError: 242 | if not 0 <= priority <= CORSAIR_LAYER_PRIORITY_MAX: 243 | return CorsairError(CorsairError.CE_InvalidArguments) 244 | 245 | return CorsairError(napi.CorsairSetLayerPriority(priority)) 246 | 247 | def get_led_luid_for_key_name(self, device_id: str, key_name: str): 248 | if not device_id or not isinstance(key_name, str): 249 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 250 | 251 | encoded = key_name.encode() 252 | if len(encoded) != 1 or not ord('A') <= encoded[0] <= ord('Z'): 253 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 254 | luid = c_uint32() 255 | err = CorsairError( 256 | napi.CorsairGetLedLuidForKeyName(to_native_id(device_id), encoded, 257 | byref(luid))) 258 | if (err == CorsairError.CE_Success): 259 | return (int(luid.value), err) 260 | return (None, err) 261 | 262 | def set_led_colors( 263 | self, device_id: str, 264 | led_colors: Collection[CorsairLedColor]) -> CorsairError: 265 | if not device_id: 266 | return CorsairError(CorsairError.CE_InvalidArguments) 267 | 268 | sz = len(led_colors) 269 | data = (CorsairLedColorNative * sz)() 270 | for i, led in enumerate(led_colors): 271 | data[i] = CorsairLedColorNative(id=int(led.id), 272 | r=led.r, 273 | g=led.g, 274 | b=led.b, 275 | a=led.a) 276 | return CorsairError( 277 | napi.CorsairSetLedColors(to_native_id(device_id), sz, data)) 278 | 279 | def set_led_colors_buffer( 280 | self, device_id: str, 281 | led_colors: Collection[CorsairLedColor]) -> CorsairError: 282 | if not device_id: 283 | return CorsairError(CorsairError.CE_InvalidArguments) 284 | 285 | sz = len(led_colors) 286 | data = (CorsairLedColorNative * sz)() 287 | for i, led in enumerate(led_colors): 288 | data[i] = CorsairLedColorNative(id=int(led.id), 289 | r=led.r, 290 | g=led.g, 291 | b=led.b, 292 | a=led.a) 293 | return CorsairError(napi.CorsairSetLedColorsBuffer( 294 | to_native_id(device_id), sz, data)) 295 | 296 | def set_led_colors_flush_buffer_async( 297 | self, 298 | callback: Optional[Callable[[CorsairError], None]]) -> CorsairError: 299 | if not callback: 300 | return CorsairError( 301 | napi.CorsairSetLedColorsFlushBufferAsync(None, None)) 302 | 303 | def raw_handler(ctx, e): 304 | err = CorsairError(e) 305 | callback(err) 306 | 307 | async_callback = CorsairAsyncCallback(raw_handler) 308 | 309 | return CorsairError( 310 | napi.CorsairSetLedColorsFlushBufferAsync(async_callback, None)) 311 | 312 | def get_led_colors(self, device_id: str, 313 | led_colors: Sequence[CorsairLedColor]): 314 | if not device_id: 315 | return (None, CorsairError(CorsairError.CE_InvalidArguments)) 316 | 317 | sz = len(led_colors) 318 | data = (CorsairLedColorNative * sz)() 319 | for i in range(sz): 320 | data[i].id = int(led_colors[i].id) 321 | err = CorsairError( 322 | napi.CorsairGetLedColors(to_native_id(device_id), sz, data)) 323 | if err == CorsairError.CE_Success: 324 | return (list([CorsairLedColor.create(data[i]) 325 | for i in range(sz)]), err) 326 | 327 | return (None, err) 328 | -------------------------------------------------------------------------------- /src/cuesdk/bin/iCUESDK.x64_2019.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CorsairOfficial/cue-sdk-python/ab0cb366013185ccb98d6af7ceb8c8de7c17501e/src/cuesdk/bin/iCUESDK.x64_2019.dll -------------------------------------------------------------------------------- /src/cuesdk/bin/libiCUESDK.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CorsairOfficial/cue-sdk-python/ab0cb366013185ccb98d6af7ceb8c8de7c17501e/src/cuesdk/bin/libiCUESDK.dylib -------------------------------------------------------------------------------- /src/cuesdk/enums.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'CorsairError', 'CorsairSessionState', 'CorsairDeviceType', 3 | 'CorsairEventId', 'CorsairDevicePropertyId', 'CorsairDataType', 4 | 'CorsairPropertyFlag', 'CorsairPhysicalLayout', 'CorsairLogicalLayout', 5 | 'CorsairChannelDeviceType', 'CorsairAccessLevel', 'CorsairLedGroup', 6 | 'CorsairLedId_Keyboard', 'CorsairMacroKeyId' 7 | ] 8 | 9 | 10 | class EnumerationType(type): 11 | 12 | def __new__(metacls, name, bases, namespace): 13 | if "_members_" not in namespace: 14 | _members_ = { 15 | k: v 16 | for k, v in namespace.items() if not k.startswith("_") 17 | } 18 | namespace["_members_"] = _members_ 19 | else: 20 | _members_ = namespace["_members_"] 21 | 22 | namespace["_reverse_map_"] = {v: k for k, v in _members_.items()} 23 | return super().__new__(metacls, name, bases, namespace) 24 | 25 | def __repr__(self): 26 | return "" % self.__name__ 27 | 28 | 29 | class Enumeration(metaclass=EnumerationType): 30 | 31 | def __init__(self, value): 32 | if value not in self._reverse_map_: 33 | raise ValueError("%d is not a valid value for %s" % 34 | (value, type(self).__name__)) 35 | self.value = value 36 | 37 | def __repr__(self): 38 | return "<%s: %d>" % (self.__str__(), self.value) 39 | 40 | def __str__(self): 41 | return "%s.%s" % (type(self).__name__, 42 | self._reverse_map_.get(self.value, '(unknown)')) 43 | 44 | def __hash__(self): 45 | return self.value 46 | 47 | def __int__(self): 48 | return self.value 49 | 50 | def __eq__(self, other): 51 | if isinstance(other, int): 52 | return self.value == other 53 | 54 | return type(self) == type(other) and self.value == other.value 55 | 56 | def __lt__(self, other): 57 | if isinstance(other, int): 58 | return self.value < other 59 | 60 | return type(self) == type(other) and self.value < other.value 61 | 62 | 63 | class CorsairError(Enumeration): 64 | CE_Success = 0 65 | CE_NotConnected = 1 66 | CE_NoControl = 2 67 | CE_IncompatibleProtocol = 3 68 | CE_InvalidArguments = 4 69 | CE_InvalidOperation = 5 70 | CE_DeviceNotFound = 6 71 | CE_NotAllowed = 7 72 | 73 | 74 | class CorsairSessionState(Enumeration): 75 | CSS_Invalid = 0 76 | CSS_Closed = 1 77 | CSS_Connecting = 2 78 | CSS_Timeout = 3 79 | CSS_ConnectionRefused = 4 80 | CSS_ConnectionLost = 5 81 | CSS_Connected = 6 82 | 83 | 84 | class CorsairDeviceType(Enumeration): 85 | CDT_Unknown = 0x0000 86 | CDT_Keyboard = 0x0001 87 | CDT_Mouse = 0x0002 88 | CDT_Mousemat = 0x0004 89 | CDT_Headset = 0x0008 90 | CDT_HeadsetStand = 0x0010 91 | CDT_FanLedController = 0x0020 92 | CDT_LedController = 0x0040 93 | CDT_MemoryModule = 0x0080 94 | CDT_Cooler = 0x0100 95 | CDT_Motherboard = 0x0200 96 | CDT_GraphicsCard = 0x0400 97 | CDT_Touchbar = 0x0800 98 | CDT_GameController = 0x1000 99 | CDT_All = 0xFFFFFFFF 100 | 101 | 102 | class CorsairEventId(Enumeration): 103 | CEI_Invalid = 0 104 | CEI_DeviceConnectionStatusChangedEvent = 1 105 | CEI_KeyEvent = 2 106 | 107 | 108 | class CorsairDevicePropertyId(Enumeration): 109 | CDPI_Invalid = 0 110 | CDPI_PropertyArray = 1 111 | CDPI_MicEnabled = 2 112 | CDPI_SurroundSoundEnabled = 3 113 | CDPI_SidetoneEnabled = 4 114 | CDPI_EqualizerPreset = 5 115 | CDPI_PhysicalLayout = 6 116 | CDPI_LogicalLayout = 7 117 | CDPI_MacroKeyArray = 8 118 | CDPI_BatteryLevel = 9 119 | CDPI_ChannelLedCount = 10 120 | CDPI_ChannelDeviceCount = 11 121 | CDPI_ChannelDeviceLedCountArray = 12 122 | CDPI_ChannelDeviceTypeArray = 13 123 | 124 | 125 | class CorsairDataType(Enumeration): 126 | CT_Boolean = 0 127 | CT_Int32 = 1 128 | CT_Float64 = 2 129 | CT_String = 3 130 | CT_Boolean_Array = 16 131 | CT_Int32_Array = 17 132 | CT_Float64_Array = 18 133 | CT_String_Array = 19 134 | 135 | 136 | class CorsairPropertyFlag(Enumeration): 137 | CPF_None = 0x00 138 | CPF_CanRead = 0x01 139 | CPF_CanWrite = 0x02 140 | CPF_Indexed = 0x04 141 | 142 | 143 | class CorsairPhysicalLayout(Enumeration): 144 | CPL_Invalid = 0 145 | CPL_US = 1 146 | CPL_UK = 2 147 | CPL_JP = 3 148 | CPL_KR = 4 149 | CPL_BR = 5 150 | 151 | 152 | class CorsairLogicalLayout(Enumeration): 153 | CLL_Invalid = 0 154 | CLL_US_Int = 1 155 | CLL_NA = 2 156 | CLL_EU = 3 157 | CLL_UK = 4 158 | CLL_BE = 5 159 | CLL_BR = 6 160 | CLL_CH = 7 161 | CLL_CN = 8 162 | CLL_DE = 9 163 | CLL_ES = 10 164 | CLL_FR = 11 165 | CLL_IT = 12 166 | CLL_ND = 13 167 | CLL_RU = 14 168 | CLL_JP = 15 169 | CLL_KR = 16 170 | CLL_TW = 17 171 | CLL_MEX = 18 172 | 173 | 174 | class CorsairChannelDeviceType(Enumeration): 175 | CCDT_Invalid = 0 176 | CCDT_HD_Fan = 1 177 | CCDT_SP_Fan = 2 178 | CCDT_LL_Fan = 3 179 | CCDT_ML_Fan = 4 180 | CCDT_QL_Fan = 5 181 | CCDT_8LedSeriesFan = 6 182 | CCDT_Strip = 7 183 | CCDT_DAP = 8 184 | CCDT_Pump = 9 185 | CCDT_DRAM = 10 186 | CCDT_WaterBlock = 11 187 | CCDT_QX_Fan = 12 188 | 189 | 190 | class CorsairAccessLevel(Enumeration): 191 | CAL_Shared = 0 192 | CAL_ExclusiveLightingControl = 1 193 | CAL_ExclusiveKeyEventsListening = 2 194 | CAL_ExclusiveLightingControlAndKeyEventsListening = 3 195 | 196 | 197 | class CorsairLedGroup(Enumeration): 198 | CLG_Keyboard = 0 199 | CLG_KeyboardGKeys = 1 200 | CLG_KeyboardEdge = 2 201 | CLG_KeyboardOem = 3 202 | CLG_Mouse = 4 203 | CLG_Mousemat = 5 204 | CLG_Headset = 6 205 | CLG_HeadsetStand = 7 206 | CLG_MemoryModule = 8 207 | CLG_Motherboard = 9 208 | CLG_GraphicsCard = 10 209 | CLG_DIY_Channel1 = 11 210 | CLG_DIY_Channel2 = 12 211 | CLG_DIY_Channel3 = 13 212 | CLG_Touchbar = 14 213 | CLG_GameController = 15 214 | 215 | 216 | class CorsairLedId_Keyboard(Enumeration): 217 | CLK_Invalid = 0 218 | CLK_Escape = 1 219 | CLK_F1 = 2 220 | CLK_F2 = 3 221 | CLK_F3 = 4 222 | CLK_F4 = 5 223 | CLK_F5 = 6 224 | CLK_F6 = 7 225 | CLK_F7 = 8 226 | CLK_F8 = 9 227 | CLK_F9 = 10 228 | CLK_F10 = 11 229 | CLK_F11 = 12 230 | CLK_F12 = 13 231 | CLK_GraveAccentAndTilde = 14 232 | CLK_1 = 15 233 | CLK_2 = 16 234 | CLK_3 = 17 235 | CLK_4 = 18 236 | CLK_5 = 19 237 | CLK_6 = 20 238 | CLK_7 = 21 239 | CLK_8 = 22 240 | CLK_9 = 23 241 | CLK_0 = 24 242 | CLK_MinusAndUnderscore = 25 243 | CLK_EqualsAndPlus = 26 244 | CLK_Backspace = 27 245 | CLK_Tab = 28 246 | CLK_Q = 29 247 | CLK_W = 30 248 | CLK_E = 31 249 | CLK_R = 32 250 | CLK_T = 33 251 | CLK_Y = 34 252 | CLK_U = 35 253 | CLK_I = 36 254 | CLK_O = 37 255 | CLK_P = 38 256 | CLK_BracketLeft = 39 257 | CLK_BracketRight = 40 258 | CLK_CapsLock = 41 259 | CLK_A = 42 260 | CLK_S = 43 261 | CLK_D = 44 262 | CLK_F = 45 263 | CLK_G = 46 264 | CLK_H = 47 265 | CLK_J = 48 266 | CLK_K = 49 267 | CLK_L = 50 268 | CLK_SemicolonAndColon = 51 269 | CLK_ApostropheAndDoubleQuote = 52 270 | CLK_Backslash = 53 271 | CLK_Enter = 54 272 | CLK_LeftShift = 55 273 | CLK_NonUsBackslash = 56 274 | CLK_Z = 57 275 | CLK_X = 58 276 | CLK_C = 59 277 | CLK_V = 60 278 | CLK_B = 61 279 | CLK_N = 62 280 | CLK_M = 63 281 | CLK_CommaAndLessThan = 64 282 | CLK_PeriodAndBiggerThan = 65 283 | CLK_SlashAndQuestionMark = 66 284 | CLK_RightShift = 67 285 | CLK_LeftCtrl = 68 286 | CLK_LeftGui = 69 287 | CLK_LeftAlt = 70 288 | CLK_Space = 71 289 | CLK_RightAlt = 72 290 | CLK_RightGui = 73 291 | CLK_Application = 74 292 | CLK_RightCtrl = 75 293 | CLK_LedProgramming = 76 294 | CLK_Lang1 = 77 295 | CLK_Lang2 = 78 296 | CLK_International1 = 79 297 | CLK_International2 = 80 298 | CLK_International3 = 81 299 | CLK_International4 = 82 300 | CLK_International5 = 83 301 | CLK_PrintScreen = 84 302 | CLK_ScrollLock = 85 303 | CLK_PauseBreak = 86 304 | CLK_Insert = 87 305 | CLK_Home = 88 306 | CLK_PageUp = 89 307 | CLK_Delete = 90 308 | CLK_End = 91 309 | CLK_PageDown = 92 310 | CLK_UpArrow = 93 311 | CLK_LeftArrow = 94 312 | CLK_DownArrow = 95 313 | CLK_RightArrow = 96 314 | CLK_NonUsTilde = 97 315 | CLK_Brightness = 98 316 | CLK_WinLock = 99 317 | CLK_Mute = 100 318 | CLK_Stop = 101 319 | CLK_ScanPreviousTrack = 102 320 | CLK_PlayPause = 103 321 | CLK_ScanNextTrack = 104 322 | CLK_NumLock = 105 323 | CLK_KeypadSlash = 106 324 | CLK_KeypadAsterisk = 107 325 | CLK_KeypadMinus = 108 326 | CLK_Keypad7 = 109 327 | CLK_Keypad8 = 110 328 | CLK_Keypad9 = 111 329 | CLK_KeypadPlus = 112 330 | CLK_Keypad4 = 113 331 | CLK_Keypad5 = 114 332 | CLK_Keypad6 = 115 333 | CLK_Keypad1 = 116 334 | CLK_Keypad2 = 117 335 | CLK_Keypad3 = 118 336 | CLK_KeypadComma = 119 337 | CLK_KeypadEnter = 120 338 | CLK_Keypad0 = 121 339 | CLK_KeypadPeriodAndDelete = 122 340 | CLK_VolumeUp = 123 341 | CLK_VolumeDown = 124 342 | CLK_MR = 125 343 | CLK_M1 = 126 344 | CLK_M2 = 127 345 | CLK_M3 = 128 346 | CLK_Fn = 129 347 | 348 | 349 | class CorsairMacroKeyId(Enumeration): 350 | CMKI_Invalid = 0 351 | CMKI_1 = 1 352 | CMKI_2 = 2 353 | CMKI_3 = 3 354 | CMKI_4 = 4 355 | CMKI_5 = 5 356 | CMKI_6 = 6 357 | CMKI_7 = 7 358 | CMKI_8 = 8 359 | CMKI_9 = 9 360 | CMKI_10 = 10 361 | CMKI_11 = 11 362 | CMKI_12 = 12 363 | CMKI_13 = 13 364 | CMKI_14 = 14 365 | CMKI_15 = 15 366 | CMKI_16 = 16 367 | CMKI_17 = 17 368 | CMKI_18 = 18 369 | CMKI_19 = 19 370 | CMKI_20 = 20 371 | -------------------------------------------------------------------------------- /src/cuesdk/helpers.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ColorRgb'] 2 | 3 | 4 | class ColorRgb(object): 5 | def __init__(self, r: int = 0, g: int = 0, b: int = 0): 6 | def validate(c): 7 | if not 0 <= c <= 255: 8 | raise ValueError("The argument must be between 0 and 255.") 9 | return int(c) 10 | 11 | self.r = validate(r) 12 | self.g = validate(g) 13 | self.b = validate(b) 14 | 15 | @property 16 | def rgb(self): 17 | return (self.r, self.g, self.b) 18 | 19 | @property 20 | def hex(self): 21 | return "0x%06x" % (((self.r & 0xff) << 16) | ((self.g & 0xff) << 8) | 22 | (self.b & 0xff)) 23 | 24 | @staticmethod 25 | def _denorm_(fnum): 26 | return 255 * max(min(fnum, 1), 0) 27 | 28 | @classmethod 29 | def from_vec3(cls, *v3): 30 | dn = cls._denorm_ 31 | col = (dn(c) for c in v3) 32 | return cls(*col) 33 | 34 | @classmethod 35 | def from_hexstr(cls, hexstr: str): 36 | val = hexstr.lstrip('#').lower().replace('0x', '').rjust(6, '0') 37 | return cls(*(int(val[i:i + 2], 16) for i in (0, 2, 4))) 38 | 39 | def __str__(self): 40 | return self.hex 41 | 42 | def __repr__(self): 43 | return "ColorRgb(%d, %d, %d)" % self.rgb 44 | 45 | def __iter__(self): 46 | return iter(self.rgb) 47 | -------------------------------------------------------------------------------- /src/cuesdk/native/__init__.py: -------------------------------------------------------------------------------- 1 | from .structs import * 2 | from .capi import * 3 | -------------------------------------------------------------------------------- /src/cuesdk/native/capi.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from ctypes import (CDLL, CFUNCTYPE, POINTER, c_bool, c_char, c_int32, 3 | c_uint32, c_void_p) 4 | 5 | from . import (CORSAIR_STRING_SIZE_M, CorsairDeviceFilter, 6 | CorsairKeyEventConfiguration, CorsairLedPosition, 7 | CorsairProperty, CorsairSessionStateChanged, 8 | CorsairSessionDetails, CorsairDeviceInfo, CorsairLedColor, 9 | CorsairEvent) 10 | 11 | __all__ = [ 12 | 'CorsairNativeApi', 'CorsairSessionStateChangedHandler', 13 | 'CorsairEventHandler', 'CorsairAsyncCallback' 14 | ] 15 | 16 | 17 | def load_library(library_path): 18 | try: 19 | return CDLL(library_path) 20 | except OSError: 21 | print("Unable to load the library %s" % library_path) 22 | sys.exit() 23 | 24 | 25 | CorsairDeviceId = c_char * CORSAIR_STRING_SIZE_M 26 | CorsairLedLuid = c_uint32 27 | CorsairError = c_uint32 28 | CorsairLedId = c_uint32 29 | CorsairAccessMode = c_uint32 30 | CorsairDevicePropertyId = c_uint32 31 | CorsairDataType = c_uint32 32 | 33 | c_bool_p = POINTER(c_bool) 34 | c_int32_p = POINTER(c_int32) 35 | c_uint32_p = POINTER(c_uint32) 36 | 37 | CorsairSessionStateChangedHandler = CFUNCTYPE( 38 | None, c_void_p, POINTER(CorsairSessionStateChanged)) 39 | CorsairAsyncCallback = CFUNCTYPE(None, c_void_p, CorsairError) 40 | CorsairEventHandler = CFUNCTYPE(None, c_void_p, POINTER(CorsairEvent)) 41 | 42 | 43 | class CorsairNativeApi(): 44 | 45 | def __init__(self, libpath): 46 | lib = load_library(libpath) 47 | 48 | def create_func(fn, restype, argtypes): 49 | f = lib.__getattr__(fn) 50 | f.restype = restype 51 | f.argtypes = argtypes 52 | return f 53 | 54 | self.CorsairConnect = create_func( 55 | 'CorsairConnect', CorsairError, 56 | [CorsairSessionStateChangedHandler, c_void_p]) 57 | 58 | self.CorsairGetSessionDetails = create_func( 59 | 'CorsairGetSessionDetails', CorsairError, 60 | [POINTER(CorsairSessionDetails)]) 61 | 62 | self.CorsairDisconnect = create_func('CorsairDisconnect', CorsairError, 63 | None) 64 | 65 | self.CorsairGetDevices = create_func( 66 | 'CorsairGetDevices', CorsairError, [ 67 | POINTER(CorsairDeviceFilter), c_int32, 68 | POINTER(CorsairDeviceInfo), c_int32_p 69 | ]) 70 | 71 | self.CorsairGetDeviceInfo = create_func( 72 | 'CorsairGetDeviceInfo', CorsairError, 73 | [CorsairDeviceId, POINTER(CorsairDeviceInfo)]) 74 | 75 | self.CorsairGetLedPositions = create_func( 76 | 'CorsairGetLedPositions', CorsairError, 77 | [CorsairDeviceId, c_int32, 78 | POINTER(CorsairLedPosition), c_int32_p]) 79 | 80 | self.CorsairSubscribeForEvents = create_func( 81 | 'CorsairSubscribeForEvents', CorsairError, 82 | [CorsairEventHandler, c_void_p]) 83 | 84 | self.CorsairUnsubscribeFromEvents = create_func( 85 | 'CorsairUnsubscribeFromEvents', CorsairError, None) 86 | 87 | self.CorsairConfigureKeyEvent = create_func( 88 | 'CorsairConfigureKeyEvent', CorsairError, 89 | [CorsairDeviceId, 90 | POINTER(CorsairKeyEventConfiguration)]) 91 | 92 | self.CorsairGetDevicePropertyInfo = create_func( 93 | 'CorsairGetDevicePropertyInfo', CorsairError, [ 94 | CorsairDeviceId, CorsairDevicePropertyId, c_uint32, 95 | POINTER(CorsairDataType), c_uint32_p 96 | ]) 97 | 98 | self.CorsairReadDeviceProperty = create_func( 99 | 'CorsairReadDeviceProperty', CorsairError, [ 100 | CorsairDeviceId, CorsairDevicePropertyId, c_uint32, 101 | POINTER(CorsairProperty) 102 | ]) 103 | 104 | self.CorsairWriteDeviceProperty = create_func( 105 | 'CorsairWriteDeviceProperty', CorsairError, [ 106 | CorsairDeviceId, CorsairDevicePropertyId, c_uint32, 107 | POINTER(CorsairProperty) 108 | ]) 109 | 110 | self.CorsairFreeProperty = create_func('CorsairFreeProperty', 111 | CorsairError, 112 | [POINTER(CorsairProperty)]) 113 | 114 | self.CorsairSetLedColors = create_func( 115 | 'CorsairSetLedColors', CorsairError, 116 | [CorsairDeviceId, c_int32, 117 | POINTER(CorsairLedColor)]) 118 | 119 | self.CorsairSetLedColorsBuffer = create_func( 120 | 'CorsairSetLedColorsBuffer', CorsairError, 121 | [CorsairDeviceId, c_int32, 122 | POINTER(CorsairLedColor)]) 123 | 124 | self.CorsairSetLedColorsFlushBufferAsync = create_func( 125 | 'CorsairSetLedColorsFlushBufferAsync', CorsairError, 126 | [CorsairAsyncCallback, c_void_p]) 127 | 128 | self.CorsairGetLedColors = create_func( 129 | 'CorsairGetLedColors', c_bool, 130 | [CorsairDeviceId, c_int32, 131 | POINTER(CorsairLedColor)]) 132 | 133 | self.CorsairSetLayerPriority = create_func('CorsairSetLayerPriority', 134 | CorsairError, [c_uint32]) 135 | 136 | self.CorsairGetLedLuidForKeyName = create_func( 137 | 'CorsairGetLedLuidForKeyName', CorsairError, 138 | [CorsairDeviceId, c_char, 139 | POINTER(CorsairLedLuid)]) 140 | 141 | self.CorsairRequestControl = create_func( 142 | 'CorsairRequestControl', CorsairError, 143 | [CorsairDeviceId, CorsairAccessMode]) 144 | 145 | self.CorsairReleaseControl = create_func('CorsairReleaseControl', 146 | CorsairError, 147 | [CorsairDeviceId]) 148 | -------------------------------------------------------------------------------- /src/cuesdk/native/structs.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | __all__ = [ 4 | 'CorsairVersion', 'CorsairSessionDetails', 'CorsairSessionStateChanged', 5 | 'CorsairDeviceInfo', 'CorsairLedPosition', 'CorsairDeviceFilter', 6 | 'CorsairDeviceConnectionStatusChangedEvent', 'CorsairKeyEvent', 7 | 'CorsairEvent', 'CorsairDataType_BooleanArray', 8 | 'CorsairDataType_Int32Array', 'CorsairDataType_Float64Array', 9 | 'CorsairDataType_StringArray', 'CorsairDataValue', 'CorsairProperty', 10 | 'CorsairLedColor', 'CorsairKeyEventConfiguration', 'CORSAIR_STRING_SIZE_S', 11 | 'CORSAIR_STRING_SIZE_M', 'CORSAIR_LAYER_PRIORITY_MAX', 12 | 'CORSAIR_DEVICE_COUNT_MAX', 'CORSAIR_DEVICE_LEDCOUNT_MAX' 13 | ] 14 | 15 | CORSAIR_STRING_SIZE_S = 64 16 | CORSAIR_STRING_SIZE_M = 128 17 | CORSAIR_LAYER_PRIORITY_MAX = 255 18 | CORSAIR_DEVICE_COUNT_MAX = 64 19 | CORSAIR_DEVICE_LEDCOUNT_MAX = 512 20 | 21 | 22 | class CorsairVersion(ctypes.Structure): 23 | _fields_ = [('major', ctypes.c_int32), ('minor', ctypes.c_int32), 24 | ('patch', ctypes.c_int32)] 25 | 26 | 27 | class CorsairSessionDetails(ctypes.Structure): 28 | _fields_ = [('clientVersion', CorsairVersion), 29 | ('serverVersion', CorsairVersion), 30 | ('serverHostVersion', CorsairVersion)] 31 | 32 | 33 | class CorsairSessionStateChanged(ctypes.Structure): 34 | _fields_ = [('state', ctypes.c_uint), ('details', CorsairSessionDetails)] 35 | 36 | 37 | class CorsairDeviceInfo(ctypes.Structure): 38 | _fields_ = [('type', ctypes.c_uint), 39 | ('deviceId', ctypes.c_char * CORSAIR_STRING_SIZE_M), 40 | ('serial', ctypes.c_char * CORSAIR_STRING_SIZE_M), 41 | ('model', ctypes.c_char * CORSAIR_STRING_SIZE_M), 42 | ('ledCount', ctypes.c_int32), ('channelCount', ctypes.c_int32)] 43 | 44 | 45 | class CorsairLedPosition(ctypes.Structure): 46 | _fields_ = [('id', ctypes.c_uint), ('cx', ctypes.c_double), 47 | ('cy', ctypes.c_double)] 48 | 49 | 50 | class CorsairDeviceFilter(ctypes.Structure): 51 | _fields_ = [('deviceTypeMask', ctypes.c_int32)] 52 | 53 | 54 | class CorsairDeviceConnectionStatusChangedEvent(ctypes.Structure): 55 | _fields_ = [('deviceId', ctypes.c_char * CORSAIR_STRING_SIZE_M), 56 | ('isConnected', ctypes.c_bool)] 57 | 58 | 59 | class CorsairKeyEvent(ctypes.Structure): 60 | _fields_ = [('deviceId', ctypes.c_char * CORSAIR_STRING_SIZE_M), 61 | ('keyId', ctypes.c_uint), ('isPressed', ctypes.c_bool)] 62 | 63 | 64 | class CorsairEventPayload(ctypes.Union): 65 | _fields_ = [('deviceConnectionStatusChangedEvent', 66 | ctypes.POINTER(CorsairDeviceConnectionStatusChangedEvent)), 67 | ('keyEvent', ctypes.POINTER(CorsairKeyEvent))] 68 | 69 | 70 | class CorsairEvent(ctypes.Structure): 71 | _anonymous_ = ("payload", ) 72 | _fields_ = [('id', ctypes.c_uint), ('payload', CorsairEventPayload)] 73 | 74 | 75 | class CorsairDataType_BooleanArray(ctypes.Structure): 76 | _fields_ = [('items', ctypes.POINTER(ctypes.c_bool)), 77 | ('count', ctypes.c_uint)] 78 | 79 | 80 | class CorsairDataType_Int32Array(ctypes.Structure): 81 | _fields_ = [('items', ctypes.POINTER(ctypes.c_int32)), 82 | ('count', ctypes.c_uint)] 83 | 84 | 85 | class CorsairDataType_Float64Array(ctypes.Structure): 86 | _fields_ = [('items', ctypes.POINTER(ctypes.c_double)), 87 | ('count', ctypes.c_uint)] 88 | 89 | 90 | class CorsairDataType_StringArray(ctypes.Structure): 91 | _fields_ = [('items', ctypes.POINTER(ctypes.c_char_p)), 92 | ('count', ctypes.c_uint)] 93 | 94 | 95 | class CorsairDataValue(ctypes.Union): 96 | _fields_ = [('boolean', ctypes.c_bool), ('int32', ctypes.c_int32), 97 | ('float64', ctypes.c_double), ('string', ctypes.c_char_p), 98 | ('boolean_array', CorsairDataType_BooleanArray), 99 | ('int32_array', CorsairDataType_Int32Array), 100 | ('float64_array', CorsairDataType_Float64Array), 101 | ('string_array', CorsairDataType_StringArray)] 102 | 103 | 104 | class CorsairProperty(ctypes.Structure): 105 | _fields_ = [('type', ctypes.c_uint), ('value', CorsairDataValue)] 106 | 107 | 108 | class CorsairLedColor(ctypes.Structure): 109 | _fields_ = [('id', ctypes.c_uint), ('r', ctypes.c_ubyte), 110 | ('g', ctypes.c_ubyte), ('b', ctypes.c_ubyte), 111 | ('a', ctypes.c_ubyte)] 112 | 113 | 114 | class CorsairKeyEventConfiguration(ctypes.Structure): 115 | _fields_ = [('keyId', ctypes.c_uint), ('isIntercepted', ctypes.c_bool)] 116 | -------------------------------------------------------------------------------- /src/cuesdk/structs.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Union 3 | from .enums import (CorsairDataType, CorsairEventId, CorsairDeviceType, 4 | CorsairMacroKeyId, CorsairSessionState) 5 | 6 | __all__ = [ 7 | 'CorsairVersion', 'CorsairSessionDetails', 'CorsairSessionStateChanged', 8 | 'CorsairDeviceInfo', 'CorsairLedPosition', 'CorsairDeviceFilter', 9 | 'CorsairDeviceConnectionStatusChangedEvent', 'CorsairKeyEvent', 10 | 'CorsairEvent', 'CorsairLedColor', 'CorsairKeyEventConfiguration', 11 | 'CorsairProperty' 12 | ] 13 | 14 | 15 | def bytes_to_str_or_default(bytes_arg, default=""): 16 | return default if not bytes_arg else bytes_arg.decode('utf-8') 17 | 18 | 19 | @dataclass(frozen=True) 20 | class CorsairVersion(): 21 | major: int 22 | minor: int 23 | patch: int 24 | 25 | @staticmethod 26 | def create(nobj): 27 | return CorsairVersion(nobj.major, nobj.minor, nobj.patch) 28 | 29 | 30 | @dataclass(frozen=True) 31 | class CorsairSessionDetails(): 32 | client_version: CorsairVersion 33 | server_version: CorsairVersion 34 | server_host_version: CorsairVersion 35 | 36 | @staticmethod 37 | def create(nobj): 38 | return CorsairSessionDetails( 39 | CorsairVersion.create(nobj.clientVersion), 40 | CorsairVersion.create(nobj.serverVersion), 41 | CorsairVersion.create(nobj.serverHostVersion)) 42 | 43 | 44 | @dataclass(frozen=True) 45 | class CorsairSessionStateChanged(): 46 | state: CorsairSessionState 47 | details: CorsairSessionDetails 48 | 49 | @staticmethod 50 | def create(nobj): 51 | return CorsairSessionStateChanged( 52 | CorsairSessionState(nobj.state), 53 | CorsairSessionDetails.create(nobj.details)) 54 | 55 | 56 | @dataclass(frozen=True) 57 | class CorsairDeviceInfo(): 58 | type: CorsairDeviceType 59 | device_id: str 60 | serial: str 61 | model: str 62 | led_count: int 63 | channel_count: int 64 | 65 | @staticmethod 66 | def create(nobj): 67 | return CorsairDeviceInfo(CorsairDeviceType(nobj.type), 68 | bytes_to_str_or_default(nobj.deviceId), 69 | bytes_to_str_or_default(nobj.serial), 70 | bytes_to_str_or_default(nobj.model), 71 | nobj.ledCount, nobj.channelCount) 72 | 73 | 74 | @dataclass(frozen=True) 75 | class CorsairLedPosition(): 76 | id: int 77 | cx: float 78 | cy: float 79 | 80 | @staticmethod 81 | def create(nobj): 82 | return CorsairLedPosition(nobj.id, nobj.cx, nobj.cy) 83 | 84 | 85 | @dataclass(frozen=True) 86 | class CorsairDeviceFilter(): 87 | device_type_mask: int 88 | 89 | @staticmethod 90 | def create(nobj): 91 | return CorsairDeviceFilter(nobj.deviceTypeMask) 92 | 93 | 94 | @dataclass(frozen=True) 95 | class CorsairDeviceConnectionStatusChangedEvent(): 96 | device_id: str 97 | is_connected: bool 98 | 99 | @staticmethod 100 | def create(nobj): 101 | return CorsairDeviceConnectionStatusChangedEvent( 102 | bytes_to_str_or_default(nobj.deviceId), nobj.isConnected) 103 | 104 | 105 | @dataclass(frozen=True) 106 | class CorsairKeyEvent(): 107 | device_id: str 108 | key_id: CorsairMacroKeyId 109 | is_pressed: bool 110 | 111 | @staticmethod 112 | def create(nobj): 113 | return CorsairKeyEvent(bytes_to_str_or_default(nobj.deviceId), 114 | CorsairMacroKeyId(nobj.keyId), nobj.isPressed) 115 | 116 | 117 | @dataclass(frozen=True) 118 | class CorsairEvent(): 119 | id: CorsairEventId 120 | data: Union[CorsairKeyEvent, CorsairDeviceConnectionStatusChangedEvent] 121 | 122 | @staticmethod 123 | def create(nobj): 124 | e = nobj 125 | id = CorsairEventId(e.id) 126 | if (id == CorsairEventId.CEI_DeviceConnectionStatusChangedEvent): 127 | return CorsairEvent( 128 | id, 129 | CorsairDeviceConnectionStatusChangedEvent.create( 130 | e.deviceConnectionStatusChangedEvent[0])) 131 | elif (id == CorsairEventId.CEI_KeyEvent): 132 | return CorsairEvent(id, CorsairKeyEvent.create(e.keyEvent[0])) 133 | raise ValueError(f"Unknown event id={id}") 134 | 135 | 136 | @dataclass 137 | class CorsairLedColor(): 138 | id: int 139 | r: int 140 | g: int 141 | b: int 142 | a: int 143 | 144 | @staticmethod 145 | def create(nobj): 146 | return CorsairLedColor(nobj.id, nobj.r, nobj.g, nobj.b, nobj.a) 147 | 148 | 149 | @dataclass(frozen=True) 150 | class CorsairKeyEventConfiguration(): 151 | key_id: CorsairMacroKeyId 152 | is_intercepted: bool 153 | 154 | @staticmethod 155 | def create(nobj): 156 | return CorsairKeyEventConfiguration(CorsairMacroKeyId(nobj.keyId), 157 | nobj.isIntercepted) 158 | 159 | 160 | @dataclass(frozen=True) 161 | class CorsairProperty(): 162 | type: CorsairDataType 163 | value: Union[bool, int, float, str, tuple] 164 | 165 | @staticmethod 166 | def create(nobj): 167 | t = CorsairDataType(nobj.type) 168 | if t == CorsairDataType.CT_Boolean: 169 | return CorsairProperty(t, nobj.value.boolean) 170 | if t == CorsairDataType.CT_Int32: 171 | return CorsairProperty(t, nobj.value.int32) 172 | if t == CorsairDataType.CT_Float64: 173 | return CorsairProperty(t, nobj.value.float64) 174 | if t == CorsairDataType.CT_String: 175 | return CorsairProperty(t, nobj.value.string) 176 | if t == CorsairDataType.CT_Boolean_Array: 177 | items = tuple(nobj.value.boolean_array.items[i] 178 | for i in range(nobj.value.boolean_array.count)) 179 | return CorsairProperty(t, items) 180 | if t == CorsairDataType.CT_Int32_Array: 181 | items = tuple(nobj.value.int32_array.items[i] 182 | for i in range(nobj.value.int32_array.count)) 183 | return CorsairProperty(t, items) 184 | if t == CorsairDataType.CT_Float64_Array: 185 | items = tuple(nobj.value.float64_array.items[i] 186 | for i in range(nobj.value.float64_array.count)) 187 | return CorsairProperty(t, items) 188 | if t == CorsairDataType.CT_String_Array: 189 | items = tuple(nobj.value.string_array.items[i] 190 | for i in range(nobj.value.string_array.count)) 191 | return CorsairProperty(t, items) 192 | raise ValueError(f"Unknown data type={t}") 193 | -------------------------------------------------------------------------------- /src/cuesdk/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "4.0.84" 2 | --------------------------------------------------------------------------------